Unity auto- factory с параметрами
Я пытаюсь выяснить правильный способ вставить auto- factory, который принимает параметры, или даже если это возможно с 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, просто не можете финансировать какие-либо четкие примеры для этого примера.
Очевидно, я мог бы создать свой собственный factory, но я ищу элегантный способ сделать это в Unity и с минимальным кодом.
Ответы
Ответ 1
Жаль, что это один из тех раздражающих людей, которые отвечают на их собственные вопросы, но я понял это.
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");
Ответ 2
Ответ @TheCodeKing отлично работает, но в большинстве (возможно, все?) случаях можно сократить до следующих значений:
Container.RegisterInstance<Func<string, ILog>>(name => new Log(name));
(обратите внимание, что я использую RegisterInstance()
вместо RegisterType()
)
Поскольку реализация Func<>
уже является своего рода factory, обычно нет необходимости обертывать ее в InjectionFactory
. Это гарантирует, что каждое разрешение Func<string, ILog>
является новым экземпляром, и я не могу думать о сценарии, который требует этого.
Ответ 3
Если вы ищете полностью типизированный интерфейс factory (например, для документации XML и имен параметров), вы можете использовать NuGet пакет, который вы можете использовать, просто определив интерфейс для factory, а затем связав его с конкретным типом, который вы хотите создать.
Код живет в GitHub: https://github.com/PombeirP/FactoryGenerator
Ответ 4
Autofac параметризует экземпляр для обработки сценариев, которым необходим автозапуск с параметрами.
Хотя Unity не поддерживает это из коробки, возможно реализовать расширение, которое будет работать аналогично Autofac.
Бесстыдный штекер: я реализовал такое расширение - Parameterized Auto Factory.
Его можно использовать таким образом, как это:
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>
предоставляет только параметр LogConsumer
string consumerName
для конструктора LogConsumer
. В результате параметр Log log
разрешен из контейнера. Если Func<string, Log, LogConsumer>
auto-factory выглядела так, как Func<string, Log, LogConsumer>
, тогда все параметры конструктора LogConsumer
были бы предоставлены через автозавод.
В основном расширение работает следующим образом:
- Он регистрирует так называемый
BuilderStrategy
in Unity. - Эта стратегия вызывается всякий раз, когда Unity собирается создать экземпляр типа.
- Если тип, который нужно создать, не был зарегистрирован явно и является параметром
Func
с параметрами, расширение перехватывает процесс создания экземпляра. - Теперь нам нужно только динамически создать
Func
данного типа, который разрешает его возвращаемый тип из контейнера и передает параметры Func
в виде коллекции ResolverOverride
в метод IUnityContainer.Resolve
.