如何在ASP.NET Core中注入泛型依赖项

19

我有以下仓库类:

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>    
{
    ...
}

我该如何在我的Startup.cs中实现ASP.NET Core的依赖注入呢?

我是这样实现它的:

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

但是我接着得到了以下错误:
无法实例化服务类型为“Test.Domain.IRepository 1 [T]”的实现类型“Test.Domain.Repository 1 [T]”。
3个回答

30

Repository<T> 是一个抽象类,因此您不能将其注册为实现,因为抽象类根本无法实例化。如果 Repository<T> 不是抽象的,则您的注册将正常工作。

如果您无法使库类非抽象,则可以注册特定的库类实现:

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

这将正确地向您的控制器注入依赖项。


2
您也可以使用通用的 AddScoped 方法,这样更简洁:services.AddScoped<IRepository<Test>, TestRepository>() - Henk Mollema
2
请注意:如果您注册一个通用实例,例如services.AddScoped(typeof(IRepository<>), typeof(GenericRepository<>)),则不能在特定情况下提供另一个覆盖通用实例的具体实例。这将生成一个错误信息:“开放式通用服务类型 'IRepository`1[T]' 需要注册开放式通用实现类型”,这是不正确的,因为您已经注册了一个开放式通用实现。 - naasking

10

我知道这已经很晚了,但我在这里发布我的解决方案,以便其他人可以参考并使用。我编写了一些扩展来注册所有泛型接口的派生类型。

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;
                }
            }
        });
}
在启动类中,您只需要像下面这样注册通用接口。
services.AddClassesAsImplementedInterface(Assembly.GetEntryAssembly(), typeof(IRepository<>));

您可以在此Github存储库中找到完整的扩展代码。


-1

正如 @dotnetom 所说,您只能将抽象类注册为服务,而不能作为实现

请参见以下定义:

 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
{

}

控制器:

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

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

如果您以这种方式注入它们:
 services.AddScoped(typeof(BaseClass<>), typeof(DerivedClass));

在解析依赖项时,您会遇到以下错误:

开放式通用服务类型 'BaseClass`1[T]' 需要注册开放式通用实现类型。(参数“描述符”)

您应该注册泛型类型或使用定义了泛型参数的服务和实现进行注册。

现在这个问题应该已经完美解决了。

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

或者

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

我个人认为,与其使用抽象类,不如选择接口定义。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接