【问题标题】:Unity auto-factory with params带参数的 Unity 自动工厂
【发布时间】:2010-09-29 19:45:55
【问题描述】:

我正在尝试找出注入带有参数的自动工厂的正确方法,或者即使使用 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 中使用最少的代码来实现这一点。

【问题讨论】:

    标签: c# unity-container


    【解决方案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");
    

    【讨论】:

    • 是否可以这样做,但使用构造函数注入?
    • @Chesheersky 上面的例子使用了构造函数注入。
    • 我不觉得“回答你自己的问题”很烦人,尤其是当它有用的时候:) 你能解释一下这个RegisterType&lt;Func&lt;string, ILog&gt;&gt;(new InjectionFactory( ..) 代码吗?这不是那么明显......我知道它可以确保 test.CreateLog("dsffads") 得到解决,但是如何?为什么
    • InjectionFactory 方法是向 Unity 注册 lambda 表达式的方法,它定义了如何构造 Func 类型的实例。 lambda 函数是一个匿名函数,它根据给定的字符串构造 ILog。
    【解决方案2】:

    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&lt;string, LogConsumer&gt; 是从 container 解析的,但它没有在任何地方注册——它是自动生成的。
    • Func&lt;string, LogConsumer&gt; 只提供string consumerName 参数给LogConsumer 的构造函数。结果,Log log 参数从容器中解析。如果自动工厂函数看起来像 Func&lt;string, Log, LogConsumer&gt;,那么 LogConsumer 的构造函数的所有参数都将通过自动工厂提供。

    基本上,扩展是这样工作的:

    1. 它在 Unity 的管道中注册了一个所谓的 BuilderStrategy
    2. 每当 Unity 要构建类型的实例时,都会调用此策略。
    3. 如果要构建的类型没有显式注册并且是带参数的Func,则扩展程序会拦截实例构建过程。
    4. 现在我们只需要动态地创建一个给定类型的Func,它从容器中解析它的返回类型并将Func的参数作为ResolverOverride集合传递给IUnityContainer.Resolve方法。李>

    【讨论】:

      【解决方案3】:

      @TheCodeKing 的回答很好,但在大多数(可能是所有?)情况下可以缩短为以下:

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

      (请注意,我使用的是RegisterInstance() 而不是RegisterType()

      由于Func&lt;&gt; 实现已经是一种工厂,因此通常不需要将其包装在InjectionFactory 中。它只保证Func&lt;string, ILog&gt;的每一个分辨率都是一个新的实例,我实在想不出需要这个的场景。

      【讨论】:

      • 功能实现不需要容器实例的场景就足够了。如果您有更多依赖解析的可能性,您可以通过InjectionFactory 调用c.Resolve&lt;IDependency&gt;(name) 来选择哪个是正确的,其中c 是容器参数。在RegisterInstance 中,容器不能作为参数访问。
      • 嘿@PiotrKasprzyk 对不起一个无耻的插件,但我实现了a Unity extension,它基本上可以从factory-func的参数中获取name并将它传递给Log的构造函数,但是解决了IDependency 来自容器(也将其传递给构造函数)。详情请关注my answer
      • @PiotrKasprzyk 为什么不从 lambda 捕获 Container 实例?无需容器参数。
      • @Ghosthack 我经常这样做,但你会遇到一些LifetimeManager 问题。例如,如果您使用子容器进行解析,您仍将使用在注册时捕获的父容器。
      • @Tipx 同意,这是个问题。感谢您的来信。
      【解决方案4】:

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

      代码位于 GitHub:https://github.com/PombeirP/FactoryGenerator

      【讨论】:

      • 正是我需要的。建议您在答案中添加一个示例,如下所示:blog.ploeh.dk/2012/03/15/ImplementinganAbstractFactory 在动态代理部分。
      • @Ghosthack 如果您查看github.com/mykolav/unitycontainer-param-autofactory,我将不胜感激,我相信它比FactoryGenerator 的优势在于它不需要任何代码生成步骤。即,它在运行时动态生成工厂。甚至不需要在容器中注册工厂。详情请见my answer
      • @Nik 当然,看起来很棒。对不起,我刚刚看到你的评论。很快我将需要在一个新项目中将默认的IoCMvvmCross 替换为Unity。我肯定会尝试你的扩展并提供一些反馈。非常感谢。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-25
      相关资源
      最近更新 更多