I am having trouble using Ninject to load my generic type implementations given the following simplified interface/class structure.
public interface IEntry {}
public class TestEntry : IEntry {}
public interface IDBConnection<T> {}
public class DBConnection<T> : IDBConnection<T> where T : IEntry {}
I am using binds within my loaded NinjectModule:
Bind<IEntry>().To<TestEntry>();
Bind(typeof(IDBConnection<>)).To(typeof(DBConnection<>));
I want to fetch an instance of DBConnection<TestEntry> with a call of:
Kernel.TryGet<IDBConnection<IEntry>>();
However this just returns an open instance type of DBConnection<IEntry> ; I have been able to return an instance of DBConnection<TestEntry> if I change my Kernel.Get call to:
Kernel.TryGet<IDBConnection<TestEntry>>();
I understand that generics are incovariant but it seems that we circumvent the entire purpose of DI/IOC if I need to stipulate the implementation of my generic class in order for Ninject to load it... So I must be either binding, fetching or understanding things incorrectly.
Furthermore I tried a different approach to binding/loading:
Bind<IEntry>().To<TestEntry>();
Bind(typeof(IDBConnection<IEntry>)).To(typeof(DBConnection<TestEntry>));
and
Kernel.TryGet<IDBConnection<IEntry>>();
However, this yields the exception:
System.InvalidCastException : Unable to cast object of type 'DBConnection
1[TestEntry]' to type 'IDBConnection1[IEntry]'.
This is because the generic type IDBConnection<IEntry> is not covariant with DBConnection<TestEntry> right?
I want to be able to Ninject DBConnection<TestEntry> into my IDBConnection<IEntry> declaration for consumption; however incovariance of generics seems to disallow this. What's the solution?
Edit: Here is a unit Test to demonstrate/explain
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);
}
Entrya domain object? Rarely do I see examples of people having their domain objects implement interfaces. - Simon Whitehead