C# Ninject-管理泛型类型的差异?
鉴于以下简化的接口/类结构,我在使用Ninject加载泛型类型实现时遇到困难C# Ninject-管理泛型类型的差异?,c#,generics,dependency-injection,ninject,dependency-management,C#,Generics,Dependency Injection,Ninject,Dependency Management,鉴于以下简化的接口/类结构,我在使用Ninject加载泛型类型实现时遇到困难 public interface IEntry {} public class TestEntry : IEntry {} public interface IDBConnection<T> {} public class DBConnection<T> : IDBConnection<T> where T : IEntry {} Ninject将始终返回您要求的类型。如果您
public interface IEntry {}
public class TestEntry : IEntry {}
public interface IDBConnection<T> {}
public class DBConnection<T> : IDBConnection<T> where T : IEntry {}
Ninject将始终返回您要求的类型。如果您请求
IDBConnection
,那么如果您请求IDBConnection
,您将得到该类型。没有超级逻辑可以分析你的代码,让你得到一个不同于你所要求的类型
但是直接请求IDBConnection之类的东西是使用Ninject的错误方式。您应该使用构造函数注入来注入它:
public class NeedDbConnection<T> {
public NeedDbConnection(IDBConnection<T> connection) { ... }
}
公共类NeedDbConnection{
公共需要的数据库连接(IDBConnection连接){…}
}
这样,您就可以获得适合该类的特定db连接。条目是否为域对象?我很少看到人们让他们的域对象实现接口的例子;我假设您也没有看到模板化域对象的示例?但是,如果模型类没有实现接口,则泛型中的子/父类是非子午的问题仍然存在。C#/Ninject不能对泛型使用协方差(对吗?)。似乎泛型和DI不能很好地结合在一起,因为不可避免地需要硬编码一个实现类ala-IKernel.TryGet()因此首先否定了DI/IOC的目的。这种设计背后的驱动力是MongoDB C#驱动程序,它是嵌入式的,并且严重依赖于模板域对象-可以在下面看到一些示例:[您将拥有多个IEntry实现吗?如果是,那么在检索IDBConnection时指定条目类型才有意义。如果不是:IDBConnection是修复的还是可以更改它的差异?如果不能,您将陷入困境。使用一个简单的IDBConnection接口如何,该接口具有泛型类型方法检索,其中TEntry:IEntry等等?我添加了一个单元测试来演示这个问题。@BatteryBackupUnit感谢您提出的支持泛型类而支持泛型方法的建议;现在就开始研究这个问题。虽然它确实简化了注入,但缺点是消费者调用有额外的责任来规定类型(在实例化之后不应该更改:/)棘手的一点是作为泛型类型param的模型类的接口。我重构了它,实现更直接。如果ninject能够处理从泛型参数实现绑定/获取泛型参数接口,那将是一件好事,但我知道这只是一个不一致的限制。
Kernel.TryGet<IDBConnection<IEntry>>();
Kernel.TryGet<IDBConnection<TestEntry>>();
Bind<IEntry>().To<TestEntry>();
Bind(typeof(IDBConnection<IEntry>)).To(typeof(DBConnection<TestEntry>));
Kernel.TryGet<IDBConnection<IEntry>>();
public interface IEntry { }
public class TestEntry : IEntry { }
public interface IDBConnection<T> where T : IEntry { }
public class DBConnection<T> : IDBConnection<T> where T : IEntry { }
class TestModule : NinjectModule
{
public override void Load()
{
Bind<IEntry>().To<TestEntry>();
Bind(typeof(IDBConnection<IEntry>)).To(typeof(DBConnection<TestEntry>));
}
}
[Test]
public void NinjectGenericLoadTest()
{
/// this loads the expected type from interfaces however is useless
/// since loaded against a "var"
///(runtime casts knowing the impl would be required to use)
StandardKernel kernel = new StandardKernel(new TestModule());
var ninjected = kernel.TryGet(typeof(IDBConnection<IEntry>));
Assert.IsInstanceOf<DBConnection<TestEntry>>(ninjected);
/// The following is what I want but it won't compile
///:"Cannot implicitly convert type 'object' to
///'EasyMongo.Contract.IReader<EasyMongo.Contract.IEasyMongoEntry>'.
/// An explicit conversion exists (are you missing a cast?)"
//kernel = new StandardKernel(new TestModule());
//IDBConnection<IEntry> ninjectedInterface = kernel.TryGet(typeof(IDBConnection<IEntry>));
//Assert.IsInstanceOf<DBConnection<Entry>>(ninjectedInterface);
/// this throws System.InvalidCastException : Unable to cast object of type
/// 'DBConnection`1[EasyMongo.Test.Base.RandomTest+Entry]'
/// to type 'IDBConnection`1[EasyMongo.Test.Base.RandomTest+IEntry]'.
/// this is due to incovariance of generic types such that DBConnection<Entry>
/// is not a IDBConnection<IEntry>
IDBConnection<IEntry> ninjectedInterface = (IDBConnection<IEntry>)kernel.TryGet(typeof(IDBConnection<IEntry>));
Assert.IsInstanceOf<DBConnection<TestEntry>>(ninjectedInterface);
}
public class NeedDbConnection<T> {
public NeedDbConnection(IDBConnection<T> connection) { ... }
}