如何使用 Ninject 将通用接口注入 MVC 控制器
How to inject a generic interface into MVC controller using Ninject
我有以下情况:
接口:
public interface ITest<T> where T:class
{
void Delete(T item);
}
抽象实现:
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) { }
}
最终class:
public class RepoTest:Test<FirstEntity>
{
public void DoSomething() { }
}
我有一个 MVC 控制器,它看起来像这样:
public abstract class MyController<T>:Controller where T:class
{
private readonly ITest<T> _test;
protected MyController(ITest<T> test)
{
_test = test;
}
}
对于每个实体,我创建了一个控制器,它继承自 MyController,并基于我希望 ninject 注入特定 class 的实体。
为此,我尝试使用此绑定:
kernel.Bind(typeof(ITest<>)).To(typeof(Test<>)).InRequestScope();
kernel.Bind(x=>x.FromAssemblyContaining(typeof(Test<>))
.SelectAllClasses()
.InheritedFrom(typeof(Test<>))
.BindToSelf());
不幸的是,我总是遇到这种错误:
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.
我如何告诉 Ninject,根据实体类型注入 class 基础?
当前编写的代码无法运行。
您有两个选择:
- 使用泛型:
因为您的控制器期望 ITest<T>
绑定到无法实例化的 abstract
class Test<T>
。
您必须制作一个具体但通用的 class Test<T>
并为 ApplicationDbContext
添加一个绑定,它将自动工作。
- 使用反射在绑定时找到正确的类型,例如:
重要!!!移除你的两个 kernel.Bind()
电话。
// 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();
}
}
注意:方法 2 目前假设所有模型和 Test<>
导数分别在一个组件中。如果不是这种情况,您需要添加更多的反射魔法来检查所有引用的程序集。
在此之后,控制器将被 RepoTest
注入。尽管老实说,方法 1. 更好 :)
我有以下情况:
接口:
public interface ITest<T> where T:class { void Delete(T item); }
抽象实现:
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) { } }
最终class:
public class RepoTest:Test<FirstEntity> { public void DoSomething() { } }
我有一个 MVC 控制器,它看起来像这样:
public abstract class MyController<T>:Controller where T:class
{
private readonly ITest<T> _test;
protected MyController(ITest<T> test)
{
_test = test;
}
}
对于每个实体,我创建了一个控制器,它继承自 MyController,并基于我希望 ninject 注入特定 class 的实体。
为此,我尝试使用此绑定:
kernel.Bind(typeof(ITest<>)).To(typeof(Test<>)).InRequestScope();
kernel.Bind(x=>x.FromAssemblyContaining(typeof(Test<>))
.SelectAllClasses()
.InheritedFrom(typeof(Test<>))
.BindToSelf());
不幸的是,我总是遇到这种错误:
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.
我如何告诉 Ninject,根据实体类型注入 class 基础?
当前编写的代码无法运行。
您有两个选择:
- 使用泛型:
因为您的控制器期望 ITest<T>
绑定到无法实例化的 abstract
class Test<T>
。
您必须制作一个具体但通用的 class Test<T>
并为 ApplicationDbContext
添加一个绑定,它将自动工作。
- 使用反射在绑定时找到正确的类型,例如:
重要!!!移除你的两个 kernel.Bind()
电话。
// 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();
}
}
注意:方法 2 目前假设所有模型和 Test<>
导数分别在一个组件中。如果不是这种情况,您需要添加更多的反射魔法来检查所有引用的程序集。
在此之后,控制器将被 RepoTest
注入。尽管老实说,方法 1. 更好 :)