views:

239

answers:

2

Consider the following:

    public Something(IInterface concreteObjectOne, IInterface concreteObjectTwo) 
    {
        this.concreteObjectOne = concreteObjectOne;
        this.concreteObjectTwo = concreteObjectTwo;
    }

How do I set set this type of binding up with Ninject? I'd try Googling the term, but as I'm not sure what this is called I can't, nor can I find anything on the Wiki about this.

Edit:

I believe this is called Convention Based Binding, as described here. However this documentation is for version 1.0 and 2.0 does not have the Only method. I'd like this to be achieved without attributes - using the convention of names or something similiar.

+4  A: 

If I may be allowed to offer some general, instead of Ninject-specific, guidance on this, I would suggest that you reconsider your design slightly. The current constructor is vague because it offers no guidance about which implementation of IInterface that goes where - I realize that this is just a mock-up of your real API, and while the real API may offer more help to the human developer in the form of aptly named parameters, a machine like a DI Container cannot infer correct usage.

Many DI Containers offer some way to address such vagueness, for example by providing attributes you can use to associate names (metadata) with each dependency. AFAIR, Ninject has Inject attributes...

However, consider a couple of alternatives:

The first alternative is to encapsulate the two similar interface instances in an Parameter Object, like this:

public interface IParameterObject
{
    IInterface ObjectOne { get; }

    IInterface ObjectTwo { get; }
}

You can now change the constructor to take an instance of IParameterObject instead of the two interface instances themselves.

public Something(IParameterObject po)
{
    this.concreteObjectOne = po.ObjectOne;
    this.concreteObjectTwo = po.ObjectTwo;
}

This means that you can push configuration of IParameterObject to the Composition Root.

Another alternative to ponder is whether it makes sense to consider the case with two instances as just a special case of a more general design that takes any number of instances. This may not always be the case, but if it is, you can change the constructor to this:

public Something(IEnumerable<IInterface> objects)

I would personally prefer any of the above suggestions over anything that uses specific Ninject features, because it forces me to make the API more explicit in general, and thus more readable and maintainable.

Mark Seemann
@Mark. You answer yesterday (thanks again) moved me to use a factory in order to use the strategy design pattern for my plugable algorithms. The factories in question now take the two concrete objects, but as I'm using the strategy pattern they are both of the same interface. Normally without IOC I'd write code like this and manually wire up the dependencies. It also makes unit testing easy. While what you suggested would work, I somehow feel it is unessary. With 2.0 I just can't seem to find the correct way of doing anything but simple binding.
Finglas
Fair enough - glad you found a solution :)
Mark Seemann
+2  A: 

In addition to the use of "Only" method, the article suggests another solution by specifying different attributes for the injected objects.

Example:

public class ObjectOneAttribute : Attribute
{

}  
public class ObjectTwoAttribute : Attribute
{

}

Then

public Something([ObjectOneAttribute] IInterface concreteObjectOne, [ObjectTwoAttribute] IInterface concreteObjectTwo) 
    {
        this.concreteObjectOne = concreteObjectOne;
        this.concreteObjectTwo = concreteObjectTwo;
    }

And when you want to bind the interface to the correct concrete object, use the "WhereTargetHas" method:

Bind<IInterface>().To<YourConcreteTypeOne>().WhereTargetHas<ObjectOneAttribute>();
Bind<IInterface>().To<YourConcreteTypeTwo>().WhereTargetHas<ObjectTwoAttribute>();

Update: Solution without using attributes:
Use the method "When":

Bind<IInterface>().To<YourConcreteTypeOne>().When(r => r.Target.Name == "concreteObjectOne");  
Bind<IInterface>().To<YourConcreteTypeTwo>().When(r => r.Target.Name == "concreteObjectTwo")

;

Marwan Aouida
I'd rather not add attributes. The end of the article shows how to make use of convention, e.g. the parameter is named X. That's what I'm after really.
Finglas
@Finglas I've updated with another solution
Marwan Aouida
That's excellent. I was close when I was playing around to getting this to work, but you're spot on. I don't have anything against attributes by the way, I just find in this scenario it's wasteful. This type of configuration is not common in my code base, so using the When method is much nicer. Cheers.
Finglas