我来自使用Ninject的IoC背景,但是,在需要在Windows和Xam.Mac之间创建可移植代码后,我选择将我的容器切换到AutoFac(主要是由于NancyFx提供了正式的Bootstrapper)。
我在Ninject生态系统中依赖的一个扩展是Ninject.Extensions.Factory,我可以注入一个抽象工厂来构建一个类的不同可能的具体实例。我的意思的一个例子可以在扩展文档中找到here。
我知道Autofac可以使用稍微不同的结构将这些工厂实现为代理,但我无法根据我的需要区分工厂构造参数。
首先,Autofac可以实现吗?另外,我觉得好像这种做法将紧密耦合重新引入代码库,尽管稍微有点迂回。这个"限制"打破隐藏的反模式是伪装的一种祝福吗?
答案 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容器而不是委托工厂......