Reputation: 594
I am pretty new to the whole DI/IoC thing, so bear with me...
I have this kind of setting:
interface IA
interface IB
interface IC
abstract class A : IA
class B : A, IB
class C : A, IC
interface IX
interface IY
interface IZ
abstract class X : IX
class Y : X, IY
class Z : X, IZ
B and C's constructors look like this:
public B(IY y);
public C(IZ z);
Now I want either B or C to be constructed, based on an already created instance of either Y or Z. Like this:
IX x = new ...; // either Y or Z, determined at runtime
// lots of code
IA a = fancyfuncoftruth<IA>(x); // creates an instance of either B or C, depending on x
Is something like this possible?
To give you a bit of background: I am trying to combine WPF's treeview, the MVVM pattern and DI.
Thanks for your time.
Upvotes: 1
Views: 1254
Reputation: 594
Pfeh, I found an answer. Probably not the best, but at least something to start.
Have a look at the following complete example: (I used NInject for this):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ninject.Core;
using Ninject.Core.Parameters;
using Ninject.Conditions;
namespace IoCTest01
{
interface IA { }
interface IB : IA { }
interface IC : IA { }
abstract class A : IA { }
class B : A, IB
{
public B(IY x)
{
Console.WriteLine("Constructor for B called!");
}
}
class C : A, IC
{
public C(IZ x)
{
Console.WriteLine("Constructor for C called!");
}
}
interface IX { }
interface IY : IX { }
interface IZ : IX { }
abstract class X : IX { }
class Y : X, IY
{
}
class Z : X, IZ
{
}
class TestModule : StandardModule
{
public override void Load()
{
Bind<IY>().To<Y>();
Bind<IZ>().To<Z>();
Bind<IA>().To<B>().Only(When.Context.Parameter<ConstructorArgumentParameter>("x").Matches(
e =>
{
return e.Value.GetType().Equals(typeof(Y));
}));
Bind<IA>().To<C>().Only(When.Context.Parameter<ConstructorArgumentParameter>("x").Matches(
e =>
{
return e.Value.GetType().Equals(typeof(Z));
}));
}
}
class Program
{
static void Main(string[] args)
{
IKernel kernel = new StandardKernel(new TestModule());
IX x1 = kernel.Get<IY>();
IX x2 = kernel.Get<IZ>();
kernel.Dispose();
// lots of code
kernel = new StandardKernel(new TestModule());
var parameters = new ParameterCollection();
parameters.Add<ConstructorArgumentParameter>(new ConstructorArgumentParameter("x", x1));
kernel.Get<IA>(parameters);
parameters = new ParameterCollection();
parameters.Add<ConstructorArgumentParameter>(new ConstructorArgumentParameter("x", x2));
kernel.Get<IA>(parameters);
}
}
}
When it is run, it displays:
Constructor for B called!
Constructor for C called!
First, I had to manually inject the already instantiated objects x1 and x2 into the kernel.Get-calls. This looked like it was enough to let NInject resolve the right entity, but as soon as I added a second Binding for IA, it complained about multiple default bindings for IA. So I had to do some contextual binding:
Bind<IA>().To<B>().Only(When.Context.Parameter<ConstructorArgumentParameter>("x").Matches(
e =>
{
return e.Value.GetType().Equals(typeof(Y));
}));
This checks if the parameter x is of type Y. If yes, this binding is used.
Again, while this is a solution, it's probably far from being optimal. I wished NInject could resolve the right type (B or C) to instantiate from the dynamic type (Y or Z) of the parameter given (x).
Ah well. :-)
Does anybody have a better solution?
To Mark: does the code explain the problem better? The Main-method should give you an overview.
Upvotes: 0
Reputation: 233317
I'm not quite sure if I understand what you are looking for, but it seems to me that you are asking whether there's any functionality that can correctly resolve IA based on a specific value of IX (x).
You would be best off implementing this using an Abstract Factory that maps instances of IX to IA.
I'd personally implement this as a custom Abstract Factory, but you can also use UsingFactory or UsingFactoryMethod of Castle Windsor:
IX x = new ...;
var container = new WindsorContainer();
container.AddFacility<FactorySupportFacility>();
container.Register(Component.For<IA>().UsingFactoryMethod(k =>
{
// Do fancy stuff with x here
// This example just shows that x can be referenced
// in the closure, but I'm not using it...
if (x == null)
{
}
return k.Resolve<B>();
}));
container.Register(Component.For<B>());
container.Register(Component.For<IY>().ImplementedBy<Y>());
var result = container.Resolve<IA>();
Upvotes: 2