Let's assume the following classes
class Foo : IFoo {
Foo(IBar bar) {}
}
class Bar : IBar {
Bar(IBaz baz)
}
My container is set up so you can differentiate on IBaz by key.
builder.RegisterType<Baz1>().Keyed<IBaz>("1");
builder.RegisterType<Baz2>().Keyed<IBaz>("2");
Now I would like to create two classes who have an IFoo injected, but further down they need to be injected with either Baz1 or 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
}
}
How do I configure/setup something like that? Preferable with an attribute in MyClassA and MyClassB.
Your question sort of hovers between two of our Autofac FAQs:
IFoo
) but you want to specify the value somewhere in the middle of the chain, something that IFoo
doesn't directly depend on.IBar
implementations based on where/how something elsewhere is being resolved.It may not be the answer you want, but... in both of these cases, the answer is that there's an issue with the design that should be addressed rather than trying to force this to happen.
We explain why this is a design problem on the 'pass a parameter' FAQ. It says "parameter" but you could read the same thing as "resolving a specific implementation of an interface." I'll update/tweak the text so it applies here:
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.
This is also basically the note on the '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 - that you should be able to swap the IBaz
implementations without breaking things. "Knowing" that one is substantially different than the other naturally implies that they're not the same and, thus, shouldn't implement the same interface. (Maybe a common base interface, but when they're consumed, the interface being consumed wouldn't be the same if the underlying implementation can't be treated the same.)
I recommend redesigning the interfaces so you don't have this problem. If that's not possible... well, honestly, there's not a much better solution for it than the answer you already posted. It's not easy to accomplish because it's not generally something you should try accomplishing.
Again, sorry that's probably not the answer you want, but I think that's the answer.