Единство автозавод с параметрами - PullRequest
16 голосов
/ 29 сентября 2010

Я пытаюсь найти правильный способ ввести авто-фабрику, которая принимает параметры, или даже если это возможно с Unity.

Например, я знаю, что могу сделать это:

public class TestLog
{
     private Func<ILog> logFactory;

     public TestLog(Func<ILog> logFactory)
     {
          this.logFactory = logFactory;
     }
     public ILog CreateLog()
     {
         return logFactory();
     }
}

Container.RegisterType<ILog, Log>();
TestLog test = Container.Resolve<TestLog>();
ILog log = test.CreateLog();

Теперь я хотел бы иметь возможность:

public class TestLog
{
     private Func<string, ILog> logFactory;

     public TestLog(Func<string, ILog> logFactory)
     {
          this.logFactory = logFactory;
     }
     public ILog CreateLog(string name)
     {
         return logFactory(name);
     }
}

Container.RegisterType<ILog, Log>();
TestLog test = Container.Resolve<TestLog>();
ILog log = test.CreateLog("Test Name");

К сожалению, это не такт работа.Я вижу, как вы можете настроить пользовательские фабрики для создания экземпляров в Unity, но не могу найти каких-либо четких примеров для этого примера.

Очевидно, Я мог бы создать свою собственную фабрику, ноЯ ищу элегантный способ сделать это в Unity и с минимальным кодом.

Ответы [ 4 ]

30 голосов
/ 30 сентября 2010

Извините, что один из тех раздражающих людей, которые отвечают на свои вопросы, но я понял это.

public class TestLog
{
    private Func<string, ILog> logFactory;

    public TestLog(Func<string, ILog> logFactory)
    {
         this.logFactory = logFactory;
    }
    public ILog CreateLog(string name)
    {
        return logFactory(name);
    }
}

Container.RegisterType<Func<string, ILog>>(
     new InjectionFactory(c => 
        new Func<string, ILog>(name => new Log(name))
     ));

TestLog test = Container.Resolve<TestLog>();
ILog log = test.CreateLog("Test Name");
7 голосов
/ 12 мая 2016

Ответ @TheCodeKing работает нормально, но в большинстве (возможно, всех?) Случаев можно сократить до следующего:

Container.RegisterInstance<Func<string, ILog>>(name => new Log(name));

(обратите внимание, что вместо этого я использую RegisterInstance()из RegisterType())

Поскольку реализация Func<> уже является своего рода фабрикой, обычно нет необходимости заключать ее в InjectionFactory.Это только гарантирует, что каждое разрешение Func<string, ILog> является новым экземпляром, и я не могу придумать сценарий, который требует этого.

1 голос
/ 31 мая 2018

Autofac имеет параметризованный экземпляр для обработки сценариев, которые требуют автоматической фабрики с параметрами.

Хотя Unity не поддерживает это "из коробки", возможно реализовать расширение, которое будет работать аналогично Autofac.

Бесстыдный плагин: я реализовал такое расширение - Параметризованный автоматический завод .
Его можно использовать аналогично следующему:

public class Log
{
    public void Info(string info) { /* ... */ }
    public void Warn(string info) { /* ... */ }
    public void Error(string info) { /* ... */ }
}

public class LogConsumer
{
    private readonly Log _log;
    private readonly string _consumerName;

    public LogConsumer(Log log, string consumerName)
    {
        _log = log;
        _consumerName = consumerName;
    }

    public void Frobnicate()
        => _log.Info($"{nameof(Frobnicate)} called on {_consumerName}");
}

var container = new UnityContainer()
    .AddNewExtension<UnityParameterizedAutoFactoryExtension>();

var logConsumerFactory = container.Resolve<Func<string, LogConsumer>>();
var gadget = logConsumerFactory("Gadget");
gadget.Frobnicate();

Обратите внимание:

  • Func<string, LogConsumer> разрешено с container, но нигде не зарегистрировано - генерируется автоматически.
  • Func<string, LogConsumer> предоставляет параметр string consumerName только конструктору LogConsumer.В результате параметр Log log разрешается из контейнера.Если бы вместо этого функция авто-фабрики выглядела так Func<string, Log, LogConsumer>, то все параметры конструктора LogConsumer были бы переданы через авто-фабрику.

По сути, расширение работает следующим образом:

  1. Оно регистрирует так называемое BuilderStrategy в конвейере Unity.
  2. Эта стратегия вызывается всякий раз, когда Unityсобирается создать экземпляр типа.
  3. Если тип создаваемого объекта не был зарегистрирован явно и представляет собой Func с параметрами, расширение перехватывает процесс создания экземпляра.
  4. Теперьнам нужно только динамически создать Func данного типа, который разрешает тип возвращаемого значения из контейнера и передает параметры Func как коллекцию ResolverOverride в метод IUnityContainer.Resolve.
1 голос
/ 24 февраля 2013

Если вам нужен полностью типизированный фабричный интерфейс (например, с учетом документации XML и имен параметров), вы можете использовать созданный мной пакет NuGet , который вы можете использовать, просто задав интерфейс для фабрики, а затем связать его с конкретным типом, который вы хотите создать.

Код живет в GitHub: https://github.com/PombeirP/FactoryGenerator

...