简体   繁体   English

Ninject-管理泛型的不变性?

[英]Ninject - Managing Inconvariance of generic types?

I am having trouble using Ninject to load my generic type implementations given the following simplified interface/class structure. 给定以下简化的接口/类结构,使用Ninject加载我的通用类型实现时遇到麻烦。

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: 我在加载的NinjectModule中使用绑定:

Bind<IEntry>().To<TestEntry>();
Bind(typeof(IDBConnection<>)).To(typeof(DBConnection<>));

I want to fetch an instance of DBConnection<TestEntry> with a call of: 我想通过以下调用获取DBConnection<TestEntry>的实例:

Kernel.TryGet<IDBConnection<IEntry>>();

However this just returns an open instance type of DBConnection<IEntry> ; 但是,这仅返回DBConnection<IEntry> 的开放实例类型 I have been able to return an instance of DBConnection<TestEntry> if I change my Kernel.Get call to: 如果将我的Kernel.Get调用更改为:我已经能够返回DBConnection<TestEntry>的实例。

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. 我知道泛型是不变的,但是如果我需要规定泛型类的实现以便Ninject加载它,那么我们似乎绕过了DI / IOC的全部目的...所以我必须是绑定,获取或理解事情不正确。

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 'IDBConnection 1[IEntry]'. System.InvalidCastException:无法将类型为“ DBConnection 1[TestEntry]' to type 'IDBConnection对象转换为类型1[TestEntry]' to type 'IDBConnection 1 [IEntry]”的对象。

This is because the generic type IDBConnection<IEntry> is not covariant with DBConnection<TestEntry> right? 这是因为通用类型IDBConnection<IEntry>DBConnection<TestEntry>不协变,对吗?

I want to be able to Ninject DBConnection<TestEntry> into my IDBConnection<IEntry> declaration for consumption; 我希望能够将DBConnection<TestEntry>注入我的IDBConnection<IEntry>声明中以供使用; 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);
    }

Ninject will always return the type you ask for. Ninject将始终返回您要求的类型。 If you ask for IDBConnection<IEntry> then you will get that type if you ask for IDBConnection<TestEntry> . 如果您要求IDBConnection<IEntry>那么如果您要求IDBConnection<TestEntry> ,则将获得该类型。 There is no super logic that will analyze your code and get you a different type then the one you are asking for. 没有超级逻辑可以分析您的代码并为您提供与您要求的类型不同的类型。

But asking for things like IDBConnection directly is the wrong way to use Ninject anyway. 但是,直接要求诸如IDBConnection之类的东西还是使用Ninject的错误方法。 You should inject it using constructor injection: 您应该使用构造函数注入来注入它:

 public class NeedDbConnection<T> {
      public NeedDbConnection(IDBConnection<T> connection) { ... } 
 }

That way you get your specific db connection appropriate for that class. 这样,您将获得适合该类的特定数据库连接。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM