Autofac 以不同的分辨率解析内部依赖
Autofac resolve with different resolution for inner dependency
让我们假设以下 类
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
}
}
我如何 configure/setup 这样的事情?最好使用 MyClassA 和 MyClassB 中的属性。
嗯,这样就可以了。
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();
但是,我不认为这是执行此类操作的首选方法。
您的问题介于我们的两个 Autofac 常见问题解答之间:
- How do I pass a parameter to the middle of a resolve chain?:因为你试图在顶层解决某些问题 (
IFoo
) 但你想在某处指定值链的中间,IFoo
不直接依赖的东西。
- How do I pick a service implementation by context?:因为您正试图在基于 where/how 的两个不同
IBar
实现之间进行选择,所以其他地方的问题正在解决中。
这可能不是您想要的答案,但是...在这两种情况下,答案是设计存在问题应该解决而不是试图强迫这种情况发生。
我们解释了为什么这是一个设计问题 on the 'pass a parameter' FAQ。它说 "parameter" 但你可以阅读与 "resolving a specific implementation of an interface." 相同的内容我将 update/tweak 文本因此它适用于此处:
Technically speaking, you’re resolving an IFoo
- a component that doesn’t need to know about the IBaz
implementation. The implementation of IFoo
could change, or even the implementation of IBar
. You could register a stub for testing, or switch up how things work so that implementation tie isn't required.
Forcing the implementation tie of IBaz
to the specific IFoo
being required breaks the decoupling that interface-based development and inversion of control gives you by assuming that you "know" how the entire dependency chain is being resolved.
这基本上也是 'implementation by context' FAQ. In that answer, there's a whole analogy using the object-oriented "animals" hierarchy to illustrate in a concrete fashion why it's not good. I'll not re-paste that here. However, I'll reiterate that treating these two IBaz
implementations differently breaks the Liskov substitution principle 上的说明 - 您应该能够在不破坏任何东西的情况下交换 IBaz
实现。 "Knowing" 一个与另一个有很大不同自然意味着它们 不 相同,因此不应实现相同的接口。 (也许是一个常见的 base 接口,但是当它们被使用时,如果底层实现不能被相同对待,那么被使用的接口将不会相同。)
我建议重新设计界面,这样您就不会遇到这个问题。 如果那不可能...嗯,老实说,没有更好的解决方案 。这并不容易完成,因为它通常不是您应该尝试完成的事情。
再次抱歉,这可能不是您想要的答案,但我认为这就是答案。
让我们假设以下 类
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
}
}
我如何 configure/setup 这样的事情?最好使用 MyClassA 和 MyClassB 中的属性。
嗯,这样就可以了。
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();
但是,我不认为这是执行此类操作的首选方法。
您的问题介于我们的两个 Autofac 常见问题解答之间:
- How do I pass a parameter to the middle of a resolve chain?:因为你试图在顶层解决某些问题 (
IFoo
) 但你想在某处指定值链的中间,IFoo
不直接依赖的东西。 - How do I pick a service implementation by context?:因为您正试图在基于 where/how 的两个不同
IBar
实现之间进行选择,所以其他地方的问题正在解决中。
这可能不是您想要的答案,但是...在这两种情况下,答案是设计存在问题应该解决而不是试图强迫这种情况发生。
我们解释了为什么这是一个设计问题 on the 'pass a parameter' FAQ。它说 "parameter" 但你可以阅读与 "resolving a specific implementation of an interface." 相同的内容我将 update/tweak 文本因此它适用于此处:
Technically speaking, you’re resolving an
IFoo
- a component that doesn’t need to know about theIBaz
implementation. The implementation ofIFoo
could change, or even the implementation ofIBar
. You could register a stub for testing, or switch up how things work so that implementation tie isn't required.Forcing the implementation tie of
IBaz
to the specificIFoo
being required breaks the decoupling that interface-based development and inversion of control gives you by assuming that you "know" how the entire dependency chain is being resolved.
这基本上也是 'implementation by context' FAQ. In that answer, there's a whole analogy using the object-oriented "animals" hierarchy to illustrate in a concrete fashion why it's not good. I'll not re-paste that here. However, I'll reiterate that treating these two IBaz
implementations differently breaks the Liskov substitution principle 上的说明 - 您应该能够在不破坏任何东西的情况下交换 IBaz
实现。 "Knowing" 一个与另一个有很大不同自然意味着它们 不 相同,因此不应实现相同的接口。 (也许是一个常见的 base 接口,但是当它们被使用时,如果底层实现不能被相同对待,那么被使用的接口将不会相同。)
我建议重新设计界面,这样您就不会遇到这个问题。 如果那不可能...嗯,老实说,没有更好的解决方案
再次抱歉,这可能不是您想要的答案,但我认为这就是答案。