I have searched this issue but with no luck. Here we go.

Suppose I have an interface:

interface IQueryRepository<T> where T : class

and I want to bind any requests for:

IQueryRepository<IClient>

to:

ConcreteQueryRepository<Client>

I've tried the obvious:

Bind<IGenericQueryRepository<IClient>>().To<ConcreteQueryRepository<Client>>()

But I get an error:

ConcreteQueryRepository<Client> cannot be used as type parameter 'TImplementation' in the generic type or method 'Ninject.Syntax.IBindingToSyntax<T>.To<TImplementation>()' There is no implicit reference conversion from 'ConcreteQueryRepository<Client>' to 'IGenericQueryRepository<IClient>'

But I don't understand why since GenericQueryRepository implements IGenericQueryRepository and Client implements IClient.

I would like Ninject to give me a concrete generic repository where T is Client. I want this to avoid using concrete types in the code.

Can it be done?

有帮助吗?

解决方案

This has to do with Covariance and Contravariance.

In your question you mentioned the following:

... GenericQueryRepository implements IGenericQueryRepository and Client implements IClient.

Let's make it simpler by using fruits: Fruit implements IFruit. We'll also create a Tree class.

public interface IFruit { }
public class Fruit : IFruit { }
public class Tree<T> where T : IFruit { }

Tree<IFruit> tree = new Tree<Fruit>() // error

This will reproduce the same kind of error you're experiencing. Why? Simple.

Though Fruit implements IFruit, an Fruit Tree doesn't implement a IFruit Tree. There is no cast possible between the Fruit Tree and the IFruit Tree, although you would expect it. They are both Trees, but with a different type parameter. The fact that their type parameters are related to each other, doesn't matter.

In other words: there is no cast possible between the Fruit Tree and the IFruit Tree, because their type parameters don't match.

In general, when casting with generics, make sure their type parameters match. However, there are a few exceptional cases. See Variance in Generic Interfaces.

In your case, you could fix it by using IClient as type parameter for the GenericQueryRepository class. Doing this will allow casting because the type parameters match. But I don't know your application architecture, so this fix might be inapplicable in your case.


EDIT: To make it easier to understand, copy paste the code below and see what the compiler says.

interface IFruit { }
class Fruit : IFruit { }
interface ITree<T> where T : IFruit { }
class Tree<T> : ITree<T> where T : IFruit { }

class Program
{
    static void Main(string[] args)
    {
        ITree<Fruit> test1 = new Tree<Fruit>();   // compiles: type parameters match
        ITree<IFruit> test2 = new Tree<Fruit>();  // fails:    type parameters don't match
        ITree<Fruit> test3 = new Tree<IFruit>();  // fails:    type parameters don't match
        ITree<IFruit> test4 = new Tree<IFruit>(); // compiles: type parameters match

        IEnumerable<IFruit> test5 = new List<Fruit>(); // compiles: this is one of the exceptional cases
    }
}

That should clear things up about what is and what is not possible.

其他提示

I've had the same problem when trying to bind a Dapper query to an interface type, thinking about it, it seems to make sense that Dapper can't instantiate an Interface type.

The interface is only a contract and does not know about how to instantiate a concrete implementation of it.

Dapper needs a type that is concrete implementation of the interface type otherwise Dapper would also have to know which concrete implementation of the interface to instantiate, and in that case Dapper would behave like a DI container which, indeed, it isn't.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top