Autofac:使用参数解决依赖关系

Joe*_*ein 2 ioc-container autofac

我目前正在学习Autofac的API,而我正试图把我的脑袋看作是一个非常常见的用例.

我有一个类(对于这个简单的例子'MasterOfPuppets'),它有一个通过构造函数注入('NamedPuppet')接收的依赖,这个依赖需要一个用(string name)构建的值:

    public class MasterOfPuppets : IMasterOfPuppets
    {
        IPuppet _puppet;

        public MasterOfPuppets(IPuppet puppet)
        {
            _puppet = puppet;
        }
    }

    public class NamedPuppet : IPuppet
    {
        string _name;

        public NamedPuppet(string name)
        {
            _name = name;
        }
    }
Run Code Online (Sandbox Code Playgroud)

我用它们的接口注册这两个类,而不是我想要解析IMasterOfPuppets,并将一个字符串注入到'NamedPuppet'的实例中.

我尝试以下列方式执行此操作:

IMasterOfPuppets master = bs.container.Resolve<IMasterOfPuppets>(new NamedParameter("name", "boby"));
Run Code Online (Sandbox Code Playgroud)

这以运行时错误结束,所以我猜Autofac只会尝试将其注入'MasterOfPuppets'.

所以我的问题是,我怎样才能解决'IMasterOfPuppets'并以最优雅的方式将参数参数传递给它的依赖?其他ioc容器有更好的解决方案吗?

Tra*_*lig 7

Autofac不支持将参数传递给父/消费者对象,并将这些参数细化为子对象.

一般来说,我要求消费者要知道其依赖关系的界面背后是什么是糟糕的设计.让我解释:

从您的设计中,您有两个接口:IMasterOfPuppetsIPuppet.在示例中,您只有一种类型IPuppet- NamedPuppet.请记住,即使拥有接口是将接口与实现分开,您可能还会在系统中使用此接口:

public class ConfigurablePuppet : IPuppet
{
  private string _name;
  public ConfigurablePuppet(string name)
  {
    this._name = ConfigurationManager.AppSettings[name];
  }
}
Run Code Online (Sandbox Code Playgroud)

有两点需要注意.

首先,当与消费者一起使用时,您应该使用不同的实现来IPuppet代替任何其他实现.在实施永远不应该知道的实施改变了... ...和消费的东西应该是进一步去除.IPuppetIMasterOfPuppetsIMasterOfPuppetsIPuppetIMasterOfPuppets

其次,示例NamedPuppet和new都ConfigurablePuppet采用具有相同名称的字符串参数,但这意味着与支持实现不同.因此,如果您的消费代码正在执行您在示例中显示的内容 - 传入一个旨在作为事物名称的参数- 那么您可能遇到了界面设计问题.见:利斯科夫替代原则.

点是,考虑到IMasterOfPuppets 执行需要一个IPuppet传入,它不应该关心如何IPuppet其构建为开始或什么是真正来头IPuppet.一旦它知道,你就会打破界面和实现的分离,这意味着你可以不再使用界面而只是一直传入NamedPuppet对象.

就传递参数而言,Autofac确实有参数支持.

推荐和最常见的参数传递类型是在注册期间,因为那时您可以在容器级别设置并且您没有使用服务位置(通常被认为是反模式).

如果您需要在分辨率期间传递参数,Autofac也支持该功能.然而,当在解决期间通过时,它更多的是服务定位器并且不是那么大,因为它再次暗示消费者知道它正在消耗什么.

如果要将参数连接到来自已知源(如配置)的连接,则可以使用lambda表达式注册执行一些奇特的操作.

builder.Register(c => {
  var name = ConfigurationManager.AppSettings["name"];
  return new NamedPuppet(name);
}).As<IPuppet>();
Run Code Online (Sandbox Code Playgroud)

您还可以使用消费者中Func<T>隐式关系来做一些奇特的事情:

public class MasterOfPuppets : IMasterOfPuppets
{
    IPuppet _puppet;

    public MasterOfPuppets(Func<string, IPuppet> puppetFactory)
    {
        _puppet = puppetFactory("name");
    }
}
Run Code Online (Sandbox Code Playgroud)

这样做相当于在分辨率期间使用TypedParameter类型string.但是,正如你所看到的那样,它来自直接消费者,IPuppet而不是通过所有决议的堆栈渗透的东西.

最后,您还可以使用Autofac模块log4net集成模块示例中的方式执行一些有趣的交叉操作.使用这样的技术允许您通过所有分辨率全局插入特定参数,但它不一定提供在运行时传递参数的能力 - 您必须将参数的源放在模块中.

要点Autofac支持参数,而不是你想要做的事情.我强烈建议你重新设计你做事的方式,这样你就不需要做你正在做的事情,或者你可以用上面提到的方法解决它.

希望这能让你朝着正确的方向前进.

  • 我不会完全同意这个设计含义.我给出的例子很简单,也许太简单了.但是在实际的解决方案中,传递某个上下文/状态并将其涓流到需要它的所有依赖项是很有意义的.无论如何,我检查了Unity的文档,这个功能实际上是以["ParameterOverrides"]的形式支持的(http://msdn.microsoft.com/en-us/library/ff660920(v = pandp.20).aspx )所以现在的问题是,Autofac或其他ioc容器中是否有类似的功能(在Unity之外,这是一个相当重的人)? (2认同)