Michael Robinson
Michael Robinson

Reputation: 21

Using Ninject to bind an interface to multiple implementations unknown at compile time

I just recently started using Ninject (v2.2.0.0) in my ASP.NET MVC 3 application. So far I'm thrilled with it, but I ran into a situation I can't seem to figure out.

What I'd like to do is bind an interface to concrete implementations and have Ninject be able to inject the concrete implementation into a constructor using a factory (that will also be registered with Ninject). The problem is that I'd like my constructor to reference the concrete type, not the interface.

Here is an example:

public class SomeInterfaceFactory<T> where T: ISomeInterface, new()
{
    public T CreateInstance()
    {
        // Activation and initialization logic here
    }
}

public interface ISomeInterface 
{
}

public class SomeImplementationA : ISomeInterface
{
    public string PropertyA { get; set; }
}

public class SomeImplementationB : ISomeInterface
{
    public string PropertyB { get; set; }
}


public class Foo 
{
    public Foo(SomeImplementationA implA) 
    {
        Console.WriteLine(implA.PropertyA);
    }
}

public class Bar
{
    public Bar(SomeImplementationB implB)
    {
        Console.WriteLine(implB.PropertyB);
    }
}

Elsewhere, I'd like to bind using just the interface:

kernel.Bind<Foo>().ToSelf();
kernel.Bind<Bar>().ToSelf();
kernel.Bind(typeof(SomeInterfaceFactory<>)).ToSelf();
kernel.Bind<ISomeInterface>().To ...something that will create and use the factory

Then, when requesting an instance of Foo from Ninject, it would see that one of the constructors parameters implements a bound interface, fetch the factory, and instantiate the correct concrete type (SomeImplementationA) and pass it to Foo's constructor.

The reason behind this is that I will have many implementations of ISomeInterface and I'd prefer to avoid having to bind each one individually. Some of these implementations may not be known at compile time.

I tried using:

kernel.Bind<ISomeInterface>().ToProvider<SomeProvider>();

The provider retrieves the factory based on the requested service type then calls its CreateInstance method, returning the concrete type:

public class SomeProvider : Provider<ISomeInterface>
{
    protected override ISomeInterface CreateInstance(IContext context)
    {
        var factory = context.Kernel.Get(typeof(SomeInterfaceFactory<>)
            .MakeGenericType(context.Request.Service));
        var method = factory.GetType().GetMethod("CreateInstance");
        return (ISomeInterface)method.Invoke();
    }
}

However, my provider was never invoked.

I'm curious if Ninject can support this situation and, if so, how I might go about solving this problem.

I hope this is enough information to explain my situation. Please let me know if I should elaborate further.

Thank you!

Upvotes: 2

Views: 1083

Answers (1)

Remo Gloor
Remo Gloor

Reputation: 32725

It seems you have misunderstood how ninject works. In case you create Foo it sees that it requires a SomeImplementationA and will try to create an instance for it. So you need to define a binding for SomeImplementationA and not for ISomeInterface.

Also most likely your implementation breaks the Dependency Inversion Princple because you rely upon concrete instances instead of abstractions.

The solution to register all similar types at once (and the prefered way to configure IoC containers) is to use configuration by conventions. See the Ninject.Extensions.Conventions extenstion.

Upvotes: 1

Related Questions