让我们假设以下课程
class Foo : IFoo {
Foo(IBar bar) {}
}
class Bar : IBar {
Bar(IBaz baz)
}
我的容器已设置好,因此您可以通过密钥区分IBaz。
builder.RegisterType<Baz1>().Keyed<IBaz>("1");
builder.RegisterType<Baz2>().Keyed<IBaz>("2");
现在,我想创建两个注入了IFoo的类,但是更进一步,它们需要注入Baz1或Baz2。
class MyClassA {
MyClassA(IFoo foo) {
var baz = foo.GetBar().GetBaz();
//baz should be of type Baz1
}
}
class MyClassB {
MyClassB(IFoo foo) {
var baz = foo.GetBar().GetBaz();
//baz should be of type Baz2
}
}
如何配置/设置类似的内容?在MyClassA和MyClassB中最好带有一个属性。
答案 0 :(得分:2)
您在两个Autofac常见问题之间徘徊的问题:
IFoo
)上解决问题,但您想在链的中间指定某个值, IFoo
不直接依赖的东西。IBar
实现中进行选择。可能不是您想要的答案,但是...在这两种情况下,答案是设计中应该解决而不是试图解决的问题迫使这种情况发生。
我们解释了为什么这是一个设计问题on the 'pass a parameter' FAQ。它说“参数”,但您可以读到与“解决接口的特定实现”相同的内容。我将更新/调整文本,使其适用于此处:
从技术上讲,您正在解决
IFoo
-一种不需要了解IBaz
实现的组件。IFoo
的实现可以更改,甚至可以更改IBar
的实现。您可以注册存根进行测试,也可以切换工作方式,从而无需实施约束。通过假设您“知道”整个依赖链的方式,将
IBaz
与所需的特定IFoo
的实现联系强制打破了基于接口的开发和控制反转给您带来的分离。正在解决。
这基本上也是'implementation by context' FAQ上的注释。在那个答案中,有一个完整的类比,使用面向对象的“动物”层次结构以一种具体的方式来说明为什么它不好。我不会在这里重新粘贴。但是,我要重申,以不同方式对待这两个IBaz
实现会破坏Liskov substitution principle-您应该能够交换IBaz
实现而不会造成麻烦。 “知道”一个与另一个实质上不同自然意味着它们不相同,因此不应实现相同的接口。 (也许是一个常见的 base 接口,但是当使用它们时,如果不能将基础实现视为相同,那么使用的接口就不会相同。)
我建议您重新设计接口,以免出现此问题。如果这不可能...那么,坦白地说,than the answer you already posted并没有更好的解决方案。这不容易实现,因为通常不应该尝试完成。
再次,很抱歉,这可能不是您想要的答案,但我认为这是答案。
答案 1 :(得分:0)
好吧,这可以解决问题。
builder.RegisterType<MyClass1>()
.WithParameter(
(pi, ctx) => pi.Name == "foo",
(pfoo, cfoo) => cfoo.Resolve<IFoo>(new ResolvedParameter(
(pbar, cbar) => pbar.Name == "bar",
(pbar, cbar) => cbar.Resolve<IBar>(new ResolvedParameter(
(pbaz, cbaz) => pbaz.Name == "baz",
(pbaz, cbaz) => cbaz.ResolveKeyed<IBaz>("1"))))))
.AsSelf();
builder.RegisterType<MyClass2>()
.WithParameter(
(pi, ctx) => pi.Name == "foo",
(pfoo, cfoo) => cfoo.Resolve<IFoo>(new ResolvedParameter(
(pbar, cbar) => pbar.Name == "bar",
(pbar, cbar) => cbar.Resolve<IBar>(new ResolvedParameter(
(pbaz, cbaz) => pbaz.Name == "baz",
(pbaz, cbaz) => cbaz.ResolveKeyed<IBaz>("2"))))))
.AsSelf();
但是,我不认为这是做这样的事情的首选方式。