Unity自动工厂及其参数

19

我试图找出正确的方法来注入带参数的自动工厂,或者说这是否在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个回答

34

很抱歉我是那些自己回答问题的烦人人之一,但我想出了解决方法。

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

可以像那样做,但使用构造函数注入吗? - dmigo
@Chesheersky 上面的示例使用了构造函数注入。 - TheCodeKing
7
我不认为“自问自答”很烦人,特别是当它有用的时候 :) 请您解释一下这段代码 RegisterType<Func<string, ILog>>(new InjectionFactory( ..)?它并不明显... 我知道它确保了 test.CreateLog("dsffads") 能够被解决,但是 怎么做到的?为什么要这么做 - Prokurors
InjectionFactory方法是您在Unity中注册lambda表达式的方式,该表达式定义了如何构造Func<string, ILog>类型的实例。Lambda函数是一个匿名函数,它从给定的字符串构造ILog。 - TheCodeKing

8

@TheCodeKing提供的答案很好,但在大多数情况下(可能是所有情况),可以简化为以下内容:

Container.RegisterInstance<Func<string, ILog>>(name => new Log(name));
< p >(请注意,我使用的是 RegisterInstance()而不是 RegisterType()

由于 Func<>实现已经是一种工厂,通常不需要将其包装在 InjectionFactory 中。它只确保每个 Func<string, ILog>的解析都是一个新实例,并且我真的想不出需要这样做的场景。


如果函数实现不需要容器实例,则场景足够。如果您有更多的依赖项解析可能性,可以通过调用InjectionFactory并使用c.Resolve<IDependency>(name)来选择正确的依赖项。其中c是容器参数。在RegisterInstance中,容器无法作为参数访问。 - Piotr Kasprzyk
1
嘿 @PiotrKasprzyk,抱歉打扰了,但我实现了一个 Unity 扩展,可以基本上从工厂函数的参数中获取 name 并将其传递给 Log 的构造函数,但是从容器中解析 IDependency(并将其也传递给构造函数)。请查看我的答案以获取详细信息。 - Nik
@PiotrKasprzyk 为什么不直接从 lambda 中捕获 Container 实例呢?这样就不需要使用容器参数了。 - Brains
1
@Ghosthack 我经常这样做,但你会遇到一些LifetimeManager的问题。例如,如果你使用一个子容器来解析下一行,你仍然会使用在注册时捕获的父容器。 - Tipx
@Tipx 同意,这是一个问题。感谢您的留言。 - Brains

2
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> 是从容器中解析出来的,但它没有在任何地方注册——它是自动生成的。
  • Func<string, LogConsumer> 只提供了 string consumerName 参数给 LogConsumer 的构造函数。因此,Log log 参数是从容器中解析出来的。如果自动工厂函数像这样 Func<string, Log, LogConsumer>,那么 LogConsumer 构造函数的所有参数都将通过自动工厂提供。
基本上,该扩展程序的工作原理如下:
它在Unity的管道中注册了一个名为 BuilderStrategy 的东西。 每当Unity要构建类型的实例时,此策略将被调用。 如果要构建的类型未被显式注册并且是带参数的 Func ,则扩展会拦截实例构建过程。 现在,我们只需要动态创建给定类型的 Func ,该函数从容器中解析其返回类型,并将 Func 的参数作为 ResolverOverride 集合传递给 IUnityContainer.Resolve 方法。

1
如果您正在寻找一个完全类型化的工厂接口(允许使用XML文档和参数名称等),您可以使用我创建的NuGet包,只需为工厂定义一个接口,然后将其与您想要实例化的具体类型相关联即可。
代码存放在GitHub中:https://github.com/PombeirP/FactoryGenerator

正是我所需要的。建议您在回答中添加一个示例,就像这里一样:http://blog.ploeh.dk/2012/03/15/ImplementinganAbstractFactory/ 中的动态代理部分。 - Brains
1
@Ghosthack
希望您能查看一下 https://github.com/mykolav/unitycontainer-param-autofactory 我认为它比[FactoryGenerator] (https://github.com/PombeirP/FactoryGenerator)更具有优势,因为它不需要进行任何代码生成步骤。也就是说,它会在运行时动态生成工厂。而且甚至不需要在容器中注册一个工厂。详见[我的回答](https://dev59.com/e2865IYBdhLWcg3wat6l#50615268)。
- Nik
1
@Nik 当然,看起来很棒。抱歉,我刚看到你的评论。很快我将需要在一个新项目中将MvvmCross的默认IoC替换为Unity。我一定会尝试你的扩展并提供一些反馈。非常感谢。 - Brains

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接