Reputation: 2881
I have the following situation:
an interface:
public interface ITest<T> where T:class
{
void Delete(T item);
}
the abstract implementation:
public abstract class Test<T>:ITest<T> where T:class
{
private readonly ApplicationDbContext _context;
protected Test(ApplicationDbContext context){
_context=context;
}
public void Delete(T item) { }
}
final class:
public class RepoTest:Test<FirstEntity>
{
public void DoSomething() { }
}
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
Reputation: 9499
The code as it is written currently won't work.
You have two options:
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.
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