2

鉴于以下简化的接口/类结构,我无法使用 Ninject 加载我的泛型类型实现。

public interface IEntry {}

public class TestEntry : IEntry {}

public interface IDBConnection<T> {}

public class DBConnection<T> : IDBConnection<T> where T : IEntry {}

我在加载的 NinjectModule 中使用绑定:

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

我想DBConnection<TestEntry>通过以下调用获取一个实例:

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

然而,这只是返回一个开放的实例类型;如果我将 Kernel.Get 调用更改为DBConnection<IEntry>DBConnection<TestEntry>

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

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

此外,我尝试了一种不同的绑定/加载方法:

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

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

但是,这会产生异常:

System.InvalidCastException:无法转换类型为“DBConnection 1[TestEntry]' to type 'IDBConnection1 [IEntry]”的对象。

这是因为泛型类型IDBConnection<IEntry>不是协变的DBConnection<TestEntry>吗?

我希望能够在DBConnection<TestEntry>IDBConnection<IEntry>的消费声明中注入 Ninject;但是泛型的不变性似乎不允许这样做。解决方案是什么?

编辑:这是一个单元测试来演示/解释

    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);
    }
4

1 回答 1

2

Ninject 将始终返回您要求的类型。如果您要求,IDBConnection<IEntry>那么如果您要求,您将获得该类型IDBConnection<TestEntry>。没有超级逻辑可以分析您的代码并为您提供与您要求的类型不同的类型。

但是直接询问像 IDBConnection 这样的东西是使用 Ninject 的错误方式。您应该使用构造函数注入来注入它:

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

这样您就可以获得适合该类的特定数据库连接。

于 2013-09-23T08:34:54.667 回答