简体   繁体   中英

How to inject dependencies of generics in ASP.NET Core

I have following repository classes:

public class TestRepository : Repository<Test>
{
    private TestContext _context;

    public TestRepository(TestContext context) : base(context)
    {
        _context = context;
    }
}

public abstract class Repository<T> : IRepository<T> where T : Entity
{
    private TestContext _context;

    public Repository(TestContext context)
    {
        _context = context;
    }
    ...
}

public interface IRepository<T>    
{
    ...
}

How do I implement the dependency injection in ASP.NET Core in my Startup.cs ?

I implemented it like this:

services.AddScoped(typeof(IRepository<>), typeof(Repository<>));

But then I get following error:

Cannot instantiate implementation type 'Test.Domain.Repository 1[T]' for service type 'Test.Domain.IRepository 1[T]'.

Repository<T> is an abstract class, so you cannot register it as an implementation, because abstract class simply cannot be instantiated. Your registration would work fine if Repository<T> was not abstract.

If you cannot make repository class non-abstract, you can register specific implementation of your repository class:

services.AddScoped(typeof(IRepository<Test>), typeof(TestRepository));

This will correctly inject dependencies to your controller.

I know this is very late but I am posting my solution here so that others can refer and use this. I have written some extensions to register all the derived types of generic interface.

public static List<TypeInfo> GetTypesAssignableTo(this Assembly assembly, Type compareType)
{
        var typeInfoList = assembly.DefinedTypes.Where(x => x.IsClass 
                            && !x.IsAbstract 
                            && x != compareType
                            && x.GetInterfaces()
                                    .Any(i => i.IsGenericType
                                            && i.GetGenericTypeDefinition() == compareType))?.ToList();

        return typeInfoList;
 }

public static void AddClassesAsImplementedInterface(
        this IServiceCollection services, 
        Assembly assembly, 
        Type compareType,
        ServiceLifetime lifetime = ServiceLifetime.Scoped)
 {
        assembly.GetTypesAssignableTo(compareType).ForEach((type) =>
        {
            foreach (var implementedInterface in type.ImplementedInterfaces)
            {
                switch (lifetime)
                {
                    case ServiceLifetime.Scoped:
                        services.AddScoped(implementedInterface, type);
                        break;
                    case ServiceLifetime.Singleton:
                        services.AddSingleton(implementedInterface, type);
                        break;
                    case ServiceLifetime.Transient:
                        services.AddTransient(implementedInterface, type);
                        break;
                }
            }
        });
}

In the startup class, you just register your generic interface like below.

services.AddClassesAsImplementedInterface(Assembly.GetEntryAssembly(), typeof(IRepository<>));

You can find the complete extension code in this Github repository.

You can register an abstract class only as a service , not as implementation as @dotnetom said.

See the definitions below

 public abstract class BaseClass<T>
{

    public BaseClass()
    {
    }
}

public class DerivedClass : BaseClass<Entity>
{
    public DerivedClass() : base() { }
}

public class DerivedClass2<T> : BaseClass<T> where T: Entity
{
   
}

public class Entity
{

}

Controller:

public class WeatherForecastController : ControllerBase
    {
        ......

        public WeatherForecastController(BaseClass<Entity> baseClass)
        {
            
        }
         ...
}

If you inject them on this way:

 services.AddScoped(typeof(BaseClass<>), typeof(DerivedClass));

you get this error when you are resolving your dependency:

Open generic service type 'BaseClass`1[T]' requires registering an open generic implementation type. (Parameter 'descriptors')

You should register the generic type or register the service and the implementation with the generic argument defined

Now this should work perfectly as well

services.AddScoped(typeof(BaseClass<>), typeof(DerivedClass2<>));

or

services.AddScoped(typeof(BaseClass<Entity>), typeof(DerivedClass2<Entity>));

IMHO I prefer to go with interface definitions rather than abstract classes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM