具有命名绑定的Autofac工厂

时间:2015-02-17 10:57:57

标签: c# inversion-of-control ninject autofac

我来自使用Ninject的IoC背景,但是,在需要在Windows和Xam.Mac之间创建可移植代码后,我选择将我的容器切换到AutoFac(主要是由于NancyFx提供了正式的Bootstrapper)。

我在Ninject生态系统中依赖的一个扩展是Ninject.Extensions.Factory,我可以注入一个抽象工厂来构建一个类的不同可能的具体实例。我的意思的一个例子可以在扩展文档中找到here

我知道Autofac可以使用稍微不同的结构将这些工厂实现为代理,但我无法根据我的需要区分工厂构造参数。

首先,Autofac可以实现吗?另外,我觉得好像这种做法将紧密耦合重新引入代码库,尽管稍微有点迂回。这个"限制"打破隐藏的反模式是伪装的一种祝福吗?

2 个答案:

答案 0 :(得分:2)

我不喜欢,我从不喜欢你引用here的工厂命名约定。在我之前正在开发的项目中,我们替换了部分Factory扩展来代替处理属性。例如,您可以使用IFactory方法Foo Create([Named] name)。我们还有其他属性,例如[MatchByType][MatchByName]。我更喜欢这种方法,因为它更加冗长,并且需要较少的先前知识才能理解正在发生的事情。

我喜欢Autofac的代表工厂。但是,我最关心的是与标准的Ninject工厂相同:它不是重构安全的,因为它将委托参数名称与构造函数参数名称匹配(因此我们为什么我们将ninject工厂调整为默认情况下按类型而不是参数名称进行匹配)。为Autofac创建工厂扩展并不复杂 - 基本上可以从Ninjects实现中复制它。

然而,工厂最干净的方法可能如下:

public class FooFactory : IFooFactory
{
    private readonly IDependency1 dependency1;
    private readonly IDependency2 dependency2;

    public FooFactory(IDependency1 dependency1, IDependency2 dependency2)
    {
        this.dependency1 = dependency1;
        this.dependency2 = dependency2
    }

    public IFoo Create(IParameter someParameter)
    {
        return new Foo(this.dependency1, this.dependency2, someParameter);
    }
}

这是重构安全的。此外,如果您使用FxCop,如果您有未使用的变量,也会收到警告。 但是,您还必须考虑这是否会导致注入工厂的依赖项的正确“生命周期范围”。保持清洁,这可能会引入额外的工厂。或者,您可以选择允许更紧密地耦合到DI容器,以直接从容器中检索所有依赖项。如果您打算使用Autofac的ILifetimescope(或Owned<T>),那么无论如何都会与容器紧密耦合。

最后,我个人选择坚持使用Autofac的委托工厂,并使用广泛的规范测试来确保一切都按预期工作/防止不完整的重命名重构。

编辑:今天在“自动生成的工厂”中遇到了另一个问题。这也不是第一次。这是一个autofac委托工厂,参数不匹配。我总觉得如果有没有使用的参数,DI容器不会警告我不太好。通常这意味着代码中存在错误。 所以,如果我可以选择,我会喜欢这样的东西:

public IFoo Create(IParameter someParameter)
{
    return new Foo(this.dependency1, this.dependency2, someParameter);
}

我认为很多DI容器已经支持这种构造函数选择的语法。为什么不为迟到?另一种方法是拆分分辨率和绑定:

Bind<Foo>().ToConstructor(c => new Foo(c.Inject, c.Inject, c.FromParameter);

然后如果工厂缺少参数,则可能抛出异常。 但它仍然需要某种匹配逻辑,仍然不一定是重构安全,等等。

答案 1 :(得分:1)

我想实际回答你的第一个问题,我认为这是#34;如何申请工厂的命名注册&#34; ;-) 据我所知,你无法根据(命名)约定或属性来做到这一点。相反,您必须注册一个专门执行此操作的工厂代理。

(旁注:使用Autofac,基本功能命名为&#34;键控&#34;服务,字符串类型键是一种特殊情况,称为&#34;命名服务&#34; - 请参阅documentation

示例:

// makes types FooLicious and FooMinable available as IFoo
// similar to ninject's Bind<IFoo>().To<FooLicious>().Named("delicious");
builder.RegisterType<FooLicious>().Named<IFoo>("delicious");
builder.RegisterType<FooMinable>().Named<IFoo>("abominable");

//factory delegate definition
public delegate IFoo FooFactory(string name);

builder.Register<FooFactory>(c => 
{
    var context = c.Resolve<IComponentContext>();
    return name => 
    {
        return context.ResolveNamed<IFoo>(name);
    };
});

FooFactory factory = container.Resolve<FooFactory>();
IFoo = factory("delicious"); // returns a FooDelicious

注意:我真的不知道为什么必须从IComponentContext检索c - 这本身就是IComponentContext。我想我已经在某个地方看过这个但我不记得在哪里。也许它根本不需要...

可悲的是,Autofac的语法需要习惯并且不像Ninject的语法那么简单...


替代方案:当然您也可以直接使用DI容器而不是委托工厂......