Lucian Bumb
Lucian Bumb

Reputation: 2881

How to inject a generic interface into MVC controller using Ninject

I have the following situation:

I have a MVC Controller, which looks like this:

 public abstract class MyController<T>:Controller where T:class 
    {
        private readonly ITest<T> _test;
        protected MyController(ITest<T> test)
        {
            _test = test;
        }
    }

For each entity, I create a controller, inherited from MyController, and base on Entity I want ninject to inject the specific class.

For this I try to use this bindings:

kernel.Bind(typeof(ITest<>)).To(typeof(Test<>)).InRequestScope();

           kernel.Bind(x=>x.FromAssemblyContaining(typeof(Test<>))
           .SelectAllClasses()
           .InheritedFrom(typeof(Test<>))
           .BindToSelf());

Unfortunatly I alwasys got this kind of errors:

Error activating ITest{Tool} No matching bindings are available, and the type is not self-bindable. Activation path: 2) Injection of dependency ITest{Tool} into parameter test of constructor of type ToolsController 1) Request for ToolsController

Suggestions: 1) Ensure that you have defined a binding for ITest{Tool}. 2) If the binding was defined in a module, ensure that the module has been loaded into the kernel. 3) Ensure you have not accidentally created more than one kernel. 4) If you are using constructor arguments, ensure that the parameter name matches the constructors parameter name. 5) If you are using automatic module loading, ensure the search path and filters are correct.

How can I tell to Ninject, to inject the class base on the Entity type?

Upvotes: 0

Views: 705

Answers (1)

zaitsman
zaitsman

Reputation: 9499

The code as it is written currently won't work.

You have two options:

  1. Use generic:

Because your controller is expecting ITest<T> which is bound to an abstract class Test<T> which can't be instantiated.

You have to make a concrete but Generic class Test<T> and add a binding for ApplicationDbContext which will automatically work.

  1. Use Reflection to find the right type at binding, e.g.:

Important!!! remove both of your kernel.Bind() calls.

 // this will find classes which, like RepoTest, are derived from Test<>
var allDerivedTypes = typeof(Test<>).Assembly.GetExportedTypes().Where(x => x.BaseType.IsGenericType && x.BaseType.GetGenericTypeDefinition() == typeof(Test<>)).ToList();

// ideally, you'd find some way to constrain all your models.
// what you need for this foreach is all of the entities that can be present in things like RepoTest
foreach(var t in typeof(Tool).Assembly.GetExportedTypes())
{
    // For each entity, get a runtime representation of Test<Entity>
    var targetType = typeof(Test<>).MakeGenericType(t);

    // Check if there is a class derived from Test<Entity>
    var potentiallyPresentImplementation = allDerivedTypes.FirstOrDefault(x => targetType == x.BaseType); // here you might want to decide how to handle multiple instances of the same generic base

    // Found one, so bind it
    if(potentiallyPresentImplementation != null)
    {
        kernel.Bind(targetType ).To(potentiallyPresentImplementation ).InRequestScope();
    }
}

Note: method 2 is currently assuming that all models and Test<> derivatives are in one assmebly, respecitvely. You'd need to add a little more reflection magic to inspect all referenced assemblies if this is not the case.

After this, the controller will get RepoTest injected. Although to be honest with you, approach 1. is better :)

Upvotes: 1

Related Questions