You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Asp.Net Core依赖注入中继承服务时如何传递构造函数参数

问题

我正在为Asp.Net Core 6应用创建一个继承自BackgroundService的服务基类:

public class MyBaseService : BackgroundService
{
    private readonly IMyCommand _command;
    private readonly ILogger<MyBaseService> _logger;
    private readonly IOtherDep _dep;

    public MyBaseService(
        IMyCommand command,
        ILogger<MyBaseService> logger,
        IOtherDep dep
    )
    {
        _command = command;
        _logger = logger;
        _dep = dep;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        <...>
        await _command.ExecuteAsync();
        <...>
    }
}

现在需要创建多个派生类并将它们注册到依赖注入容器中。请问当前的实现方式是否正确?如何在不传递IServiceProvider(这被认为是反模式)的情况下,在依赖注入中使用构造函数参数?

派生类示例:

public class MyChildService1 : MyBaseService
{
    public MyChildService1(
       IMyCommand cmd,
        <?>) 
        : base(cmd, <?>)
    {

    }
}

DI注册代码:

services.AddHostedService<MyChildService1>(sp =>
{
    var param = <...>

    return new MyChildService1(
        new MyCommand1(param),
        <?>
    );
});
services.AddHostedService<MyChildService2>(...);
解决方案

当前实现的核心问题

  1. 直接在DI注册工厂中new MyCommand1(param)会绕过依赖注入容器,导致MyCommand1内部的依赖无法被容器自动管理,也不利于后续测试和维护。
  2. 派生类构造函数未正确传递基类所需的ILogger<MyBaseService>和IOtherDep依赖,需要手动从容器获取,容易出错且不符合DI设计原则。

正确实现方式

1. 优化派生类构造函数

派生类需在构造函数中接收基类所需的所有依赖,通过base关键字传递给基类。若派生类需要绑定特定命令实现,可直接注入具体命令类型(而非通用接口):

// 绑定MyCommand1的派生服务
public class MyChildService1 : MyBaseService
{
    public MyChildService1(
        MyCommand1 cmd,
        ILogger<MyBaseService> logger,
        IOtherDep dep
    ) : base(cmd, logger, dep)
    {
        // 派生类自定义逻辑可在此添加
    }
}

// 无需绑定特定命令的派生服务
public class MyChildService2 : MyBaseService
{
    public MyChildService2(
        IMyCommand cmd,
        ILogger<MyBaseService> logger,
        IOtherDep dep
    ) : base(cmd, logger, dep)
    {
    }
}

2. 规范DI注册流程

方式一:注册特定命令实现并自动注入

先将命令实现注册到容器,再直接注册托管服务,容器会自动解析所有构造函数依赖:

// 注册MyCommand1,通过容器获取配置参数
services.AddSingleton<MyCommand1>(sp => 
{
    var config = sp.GetRequiredService<IConfiguration>();
    var param = config.GetValue<string>("Command1:Param");
    return new MyCommand1(param);
});

// 直接注册托管服务,容器自动注入所有依赖
services.AddHostedService<MyChildService1>();

// 同理注册MyChildService2对应的命令和服务
services.AddSingleton<MyCommand2>(sp => 
{
    var param = sp.GetRequiredService<IOtherParamSource>().GetParam();
    return new MyCommand2(param);
});
services.AddHostedService<MyChildService2>();

方式二:泛型基类简化多服务注册

若派生服务逻辑高度一致,仅命令实现不同,可将基类改为泛型:

// 泛型基类
public class BaseHostedService<TCommand> : BackgroundService 
    where TCommand : IMyCommand
{
    private readonly TCommand _command;
    private readonly ILogger<BaseHostedService<TCommand>> _logger;
    private readonly IOtherDep _dep;

    public BaseHostedService(
        TCommand command,
        ILogger<BaseHostedService<TCommand>> logger,
        IOtherDep dep
    )
    {
        _command = command;
        _logger = logger;
        _dep = dep;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        <...>
        await _command.ExecuteAsync();
        <...>
    }
}

// 派生类无需额外逻辑
public class MyChildService1 : BaseHostedService<MyCommand1>
{
    public MyChildService1(
        MyCommand1 command,
        ILogger<BaseHostedService<MyCommand1>> logger,
        IOtherDep dep
    ) : base(command, logger, dep)
    {
    }
}

注册时同样只需注册命令和托管服务:

services.AddSingleton<MyCommand1>(sp => 
{
    var param = sp.GetRequiredService<IConfiguration>().GetValue<string>("Command1:Param");
    return new MyCommand1(param);
});
services.AddHostedService<MyChildService1>();

3. 避免IServiceProvider反模式的核心原则

  • 所有依赖通过构造函数注入,不在构造函数或业务逻辑中手动使用IServiceProvider。
  • 容器负责管理所有对象生命周期,避免手动new需要依赖的类型。
  • 传递配置参数优先使用Options模式(如IOptions<Command1Settings>),而非硬编码或手动传递。

内容的提问来源于stack exchange,提问作者egeo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 14:34:54