在 .NET Core 中定义特定通用服务的注入服务

Define Injected service on specific generic services in .NET Core

我正在寻找一种在 .NET Core 中通过以下示例使用 DI 的正确方法。

我有 LoggerStandardService 继承自 ILogger 服务。 它很好地注入了服务(EntityRepository<ModelA>EntityRepository<ModelB>),但我想定义另一个 LoggerCustomService 来注入一些特定的通用服务,例如 EntityRepository<ModelC>

这里是执行这个王者的代码。

public interface IEntityRepository<T> where T : class { }

public class EntityRepository<T> : IEntityRepository<T>
    where T : class
{
    public EntityRepository(IDbContext context, ILogger logger)
    {
        //Do some stuff
    }
}

public interface ILogger { }

public class LoggerStandardService : ILogger { }

public class LoggerCustomService : ILogger { }

在Startup.cs中:

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IDbContext, DbContext>();
    services.AddScoped<ILogger, LoggerStandardService>();
    services.AddScoped<LoggerCustomService>();

    services.AddScoped<IEntityRepository<ModelA>, EntityRepository<ModelA>>();
    services.AddScoped<IEntityRepository<ModelB>, EntityRepository<ModelB>>();

    services.AddScoped<IEntityRepository<ModelC>, EntityRepository<ModelC>>
    (x =>
        new EntityRepository<ModelC>(
                x.GetRequiredService<IDbContext>(),
                x.GetRequiredService<LoggerCustomService>()));

这是执行此操作的正确方法吗?避免在 EntityRepository<ModelC> 构造函数中定义所有其他注入服务。

您要完成的任务的正确术语是基于上下文的注入或上下文注入,您将依赖项的实现基于其上下文(例如,它的父项,如您的情况)。

Microsoft 的内置 DI 容器 (MS.DI) 对此没有很好的支持,这通常意味着您必须使用变通办法,就像您应用的变通办法一样。像您这样的解决方法的缺点是无法在通用类型上使用自动注册,例如:

services.AddScoped(typeof(IEntityRepository<>), typeof(EntityRepository<>));

通过此注册,您告诉 MS.DI 将 IEntityRepository<T> 的任何封闭通用版本映射到 EntityRepository<T> 的兼容封闭通用实现。它使您不必为 IEntityRepository<ModelA>IEntityRepository<ModelB>IEntityRepository<ModelC>IEntityRepository<ModelD> 等进行注册。此功能是许多 DI 容器具有的极其重要的功能,因为当您的应用程序中有很多模型时,它会在您的 DI 配置中节省大量维护工作。

但不幸的是,由于 MS.DI 的限制,您的情况很不幸。每个关闭的 IEntityRepository<T> 注册都必须显式完成,例如使用您已经展示的方法。

另一种选择是利用 ActivatorUtilities class。这使得单个 EntityRepository<ModelC> 注册更易于维护,因为它允许您仅指定一个被覆盖的依赖项:

services.AddScoped<IEntityRepository<ModelC>>(x =>
    ActivatorUtilities.CreateInstance<EntityRepository<ModelC>>(x,
        x.GetRequiredService<LoggerCustomService>()));

ActivatorUtilities.CreateInstance<T> 允许创建提供的 T 的新实例,同时从提供的服务提供者 x 解析其构造函数的所有依赖项,除非显式提供依赖项,这使用第二个参数 x.GetRequiredService<LoggerCustomService>().

完成