Here is a simplification of my scenario.
I have some interfaces IFoo
, IBar
, and IBaz
with their implementations Foo
, Bar
and Baz
.
Foo
depends on IBar
and Bar
depends on IBaz
, so the dependency graph looks like this:
Foo
|
Bar
|
Baz
My Windsor config looks like this, and everything works as expected:
container.Register(Component.For<IFoo>().ImplementedBy<Foo>());
container.Register(Component.For<IBar>().ImplementedBy<Bar>());
container.Register(Component.For<IBaz>().ImplementedBy<Baz>());
Now I need to introduce a new SpecialBaz
, and I want to use it instead of the old Baz
when my dependency graph begins with a Foo
. The rest of the app will continue to the the old Baz
:
Foo SomethingElse
| |
Bar Bar
| |
SpecialBaz Baz
I have tried naming the SpecialBaz
with the ServiceOverride
api:
container.Register(Component.For<IBaz>().ImplementedBy<Baz>().IsDefault());
container.Register(Component.For<IBaz>().Named("special baz").ImplementedBy<SpecialBaz>());
container.Register(Component.For<IFoo>()
.ImplementedBy<Foo>()
.DependsOn(ServiceOverride.ForKey<IBaz>().Eq("special baz")));
And specifying the dependency:
container.Register(Component.For<IBaz>().ImplementedBy<Baz>().IsDefault());
container.Register(Component.For<IBaz>().ImplementedBy<SpecialBaz>());
container.Register(Component.For<IFoo>()
.ImplementedBy<Foo>()
.DependsOn(Component.For<IBaz>().ImplementedBy<SpecialBaz>()));
But neither approach worked.
Is this possible in Castle Windsor, and how do I wire it up?
The short answer is yes - Castle Windsor can. Every DI container can. I will offer you a rule to remember by which you can resolve most of your dependency problems.
The long answer.
The rule : Every point of indirection requires a layer of abstraction. Keep this rule in mind when you try to build dependency resolutions in apps. And there are two kinds of indirections as far as I know - static and dynamic.
You need to change the graph at one point and only once (indirections) but somehow you need to traverse the dependecies graph at runtime to see if your Baz is requested for Foo or not. So you need a factory of some kind(the abstraction thingie).
You have two options depending on the answer of one question:
Is my change dependent on static or dynamic conditions?
For example - If you depend on incoming user data or environment data - you are in the dynamic case in which you will need yo use Dynamic Parameters or something else capable.
In your case your dependency is static.
and I want to use it instead of the old Baz when my dependency graph begins with a Foo.
The condition for special Baz never changes once the beginning of the graph is Foo.
You can achieve this with Factory method or Typed Factory Componend Selector. I provide you an example with Factory Method :
using System;
using Castle.MicroKernel.Registration;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var container = new Castle.Windsor.WindsorContainer();
container.Register(Component.For<IFoo>().ImplementedBy<Foo>());
container.Register(Component.For<ISomethingElse>().ImplementedBy<SomethingElse>());
container.Register(Component.For<IBar>().ImplementedBy<Bar>().LifeStyle.Transient);
container.Register
(
Component
.For<IBaz>()
.UsingFactoryMethod
(
(k, c) =>
{
IBaz resolved = null;
var requestingType = c.Handler.ComponentModel.Implementation;
if (requestingType == typeof(Foo))
{
resolved = new Baz();
}
else
{
resolved = new BazSpecial();
}
return resolved;
}
).LifeStyle.Transient
);
var f = container.Resolve<IFoo>();
var se = container.Resolve<ISomethingElse>();
Console.ReadLine();
}
}
internal interface IBar
{
}
internal interface IBaz
{
}
internal interface IFoo
{
}
interface ISomethingElse
{
}
internal class Bar : IBar
{
private readonly IBaz baz;
public Bar(IBaz baz)
{
this.baz = baz;
}
}
internal class Baz : IBaz
{
}
internal class BazSpecial : IBaz
{
}
internal class Foo : IFoo
{
private readonly IBar bar;
public Foo(IBar bar)
{
this.bar = bar;
}
}
internal class SomethingElse : ISomethingElse
{
private readonly IBar bar;
public SomethingElse(IBar bar)
{
this.bar = bar;
}
}
}}