团结汽车工厂与params

时间:2010-09-29 19:45:55

标签: c# unity-container

我正在试图找出注入自动工厂的正确方法,该工厂需要使用参数,或者即使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中以最少的代码执行此操作。

3 个答案:

答案 0 :(得分:30)

很抱歉成为那些讨厌回答自己问题的人之一,但我想出来了。

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");

答案 1 :(得分:1)

如果您正在寻找完全类型的工厂界面(例如,允许使用XML文档和参数名称),您可以使用我创建的NuGet package,您只需通过定义接口即可工厂,然后将其与您想要实例化的具体类型相关联。

代码存在于GitHub中:https://github.com/PombeirP/FactoryGenerator

答案 2 :(得分:1)

Autofacparameterized instantiation来处理需要带参数的自动出厂的方案。

虽然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>仅向string consumerName的构造函数提供LogConsumer参数。因此,从容器中解析Log log参数。如果auto-factory func看起来像这个Func<string, Log, LogConsumer>,那么LogConsumer的构造函数的所有参数都将通过自动工厂提供。

基本上,扩展程序的工作原理如下:

  1. 它在Unity的管道中注册了一个所谓的BuilderStrategy
  2. 只要Unity要构建类型的实例,就会调用此策略。
  3. 如果要构建的类型尚未明确注册并且是带参数的Func,则扩展会拦截实例构建过程。
  4. 现在我们只需要动态创建给定类型的Func,从容器中解析其返回类型,并将Func的参数作为ResolverOverride集合传递给{ {1}}方法。