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>ですよね?

消費の宣言に NinjectDBConnection<TestEntry>できるようにしたい。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);
    }
4

1 に答える 1

2

Ninject will always return the type you ask for. If you ask for IDBConnection<IEntry> then you will get that type if you ask for 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. 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.

于 2013-09-23T08:34:54.667 に答える