mike
mike

Reputation: 1744

Autofac: The type '...' is not assignable to service '...'.

Consider the following classes and interfaces:

interface IFactory<T>{}
class Factory<T> : IFactory<T>{ }

interface IEntity{}
class Entity : IEntity{ }

I would like Autofac to resolve IFactory<IEntity> to Factory<Entity> like this:

b.RegisterType<Factory<Entity>>().As<IFactory<IEntity>>();

But I get the following exception (abbreviated for clarity):

The type 'Factory`1[Entity]' is not assignable to service 'IFactory`1[[IEntity]]'

Why is that and how can the issue be resolved? Or am I trying something "wrong"?

I briefly looked into RegisterGeneric, but I don't think it applies here; also, because the above is just an example. In other cases I may want to define a different component for IFactory<IEntity>.

Upvotes: 2

Views: 1624

Answers (1)

Jon Skeet
Jon Skeet

Reputation: 1500505

This isn't an Autofac issue - it's a matter of generic variance. You'd see exactly the same in a simple C# program:

public class Program 
{
    public static void Main() 
    {
        IFactory<IEntity> factory = new Factory<Entity>();
    }
}

Most generic interfaces are invariant - the type arguments have to match exactly. Some are covariant like IEnumerable<T>, allowing you to write:

IEnumerable<object> objects = new List<string>();

and some are contravariant like IComparer<T>, allowing you to write:

IComparer<string> = Comparer<object>.Default;

Covariant interfaces only allow their type parameters to come "out" of the implementation (e.g. via return types). Contravariant interfaces only allow their type parameters to go "into" the implementation (e.g. via regular parameters). It gets subtle when you have delegate parameters that themselves accept values etc, but we'll ignore that for now...

It sounds like your IFactory<T> should be covariant - so you just change the declaration like this:

interface IFactory<out T>{}

At that point, the code compiles and I'd hope that Autofac can handle it too. That does require that your interface never uses T as an input though. (We can't tell as you haven't shown any interface members.)

For more details on generic variance, see the MS documentation.

Upvotes: 5

Related Questions