Home > Software design >  .net core dependency injection breaks with generic interface
.net core dependency injection breaks with generic interface

Time:09-14

Basically we have a Interface that takes another interface (constrained) When you create a hard implementation of that interface through a base class, the conversion fails as there is no implicit conversion.

I know that was probably hard to follow so we've gone ahead and made the simplest .NET 6 console app possible to show this issue.

Definition:

public interface IClient { }

public abstract class ClientBase : IClient { }

public class Client : ClientBase, IClient { }

public interface IServer<TClient> where TClient : IClient { }

public abstract class ServerBase<TClient>
    : IServer<TClient> where TClient : IClient { }

public class Server : ServerBase<Client> { }

Console:

using Microsoft.Extensions.DependencyInjection;

namespace ConsoleApp;

public class Program
{
    public static IServiceCollection Services = new ServiceCollection();
    public static ServiceProvider Provider;

    public static void Main()
    {
        Services.AddSingleton<IClient, Client>();
        Services.AddSingleton<IServer<IClient>, Server>();

        Provider = Services.BuildServiceProvider();
        var server = Provider.GetService<IServer<IClient>>();
    }
}

Server can be type casted to an IServer

Client can be type casted to an IClient

But you cannot implicitly type case a Server instance to an IServer<IClient> even though the base implementations all can be casted to those types.

I'm absolutely confused about this. We can fix it by changing DI to take an IServer<Client> but then one of our services which looks for ALL implementations of IServer<IClient> cannot work anymore since we are providing a concrete implementation.

CodePudding user response:

The problem is that the base class is not IServer<IClient> but defined as IServer<Client>. Which cannot be implicitly converted. This is a common problem when using generics. :-(

This is a covariant problem. It will work when you define it with out:

public interface IServer<out TClient> where TClient : IClient
{

}

Covariance enables you to use a more derived type than that specified by the generic parameter. This allows for implicit conversion of classes that implement covariant interfaces and implicit conversion of delegate types.

  • Related