SuperAPI AI 工具集

dependency-injection-patterns

编程与开发

查看项目

更新于 10-15 08:11

技能介绍

用途

该技能用于在 ASP.NET Core 应用中组织依赖注入(DI)的服务注册。当 Program.cs / Startup.cs 因大量注册而变得臃肿、难以查找相关注册、无法在测试中复用配置,或需要设计可与 Microsoft.Extensions.DependencyInjection 集成的库时,可使用本技能。

主要能力

  • 将相关服务注册分组为 IServiceCollection 扩展方法,形成可组合的 Add* 方法。
  • 支持带配置参数的扩展方法,例如绑定配置节、校验选项。
  • 提供文件组织与命名约定建议。
  • 支持分层组合扩展方法,适用于较大应用。
  • 支持 Akka.Hosting 的 actor 配置复用同一模式。
  • 说明生命周期管理(Singleton、Scoped、Transient)的适用场景。
  • 列出常见错误与反模式。

使用方式

基本结构:在功能命名空间下创建静态类,提供返回 IServiceCollection 的扩展方法,内部完成注册后返回 services 以便链式调用。

namespace MyApp.Users;

public static class UserServiceCollectionExtensions
{
    public static IServiceCollection AddUserServices(this IServiceCollection services)
    {
        services.AddScoped<IUserRepository, UserRepository>();
        services.AddScoped<IUserReadStore, UserReadStore>();
        services.AddScoped<IUserWriteStore, UserWriteStore>();
        services.AddScoped<IUserService, UserService>();
        services.AddScoped<IUserValidationService, UserValidationService>();

        return services;
    }
}

带配置的写法:

namespace MyApp.Email;

public static class EmailServiceCollectionExtensions
{
    public static IServiceCollection AddEmailServices(
        this IServiceCollection services,
        string configSectionName = "EmailSettings")
    {
        services.AddOptions<EmailOptions>()
            .BindConfiguration(configSectionName)
            .ValidateDataAnnotations()
            .ValidateOnStart();

        services.AddSingleton<IMjmlTemplateRenderer, MjmlTemplateRenderer>();
        services.AddSingleton<IEmailLinkGenerator, EmailLinkGenerator>();
        services.AddScoped<IUserEmailComposer, UserEmailComposer>();
        services.AddScoped<IEmailSender, SmtpEmailSender>();

        return services;
    }
}

在 Program.cs 中组合:

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddUserServices()
    .AddOrderServices()
    .AddEmailServices()
    .AddPaymentServices()
    .AddValidators();

var app = builder.Build();

文件组织:将扩展方法放在其注册的服务附近,约定为 {Feature}ServiceCollectionExtensions.cs。

src/
  MyApp.Api/
    Program.cs                    # Composes all Add* methods
  MyApp.Users/
    Services/
      UserService.cs
    UserServiceCollectionExtensions.cs   # AddUserServices()
  MyApp.Orders/
    OrderServiceCollectionExtensions.cs  # AddOrderServices()
  MyApp.Email/
    EmailServiceCollectionExtensions.cs  # AddEmailServices()

命名约定:Add{Feature}Services() 用于一般功能注册;Add{Feature}() 为无歧义时的简写;Configure{Feature}() 用于主要设置选项;Use{Feature}() 用于 IApplicationBuilder 上的中间件。

分层扩展:较大应用可分层组合。

public static class AppServiceCollectionExtensions
{
    public static IServiceCollection AddAppServices(this IServiceCollection services)
    {
        return services
            .AddDomainServices()
            .AddInfrastructureServices()
            .AddApiServices();
    }
}

public static class DomainServiceCollectionExtensions
{
    public static IServiceCollection AddDomainServices(this IServiceCollection services)
    {
        return services
            .AddUserServices()
            .AddOrderServices()
            .AddProductServices();
    }
}

Akka.Hosting 集成:同一模式可用于 Akka.NET actor 配置。

public static class OrderActorExtensions
{
    public static AkkaConfigurationBuilder AddOrderActors(
        this AkkaConfigurationBuilder builder)
    {
        return builder
            .WithActors((system, registry, resolver) =>
            {
                var orderProps = resolver.Props<OrderActor>();
                var orderRef = system.ActorOf(orderProps, "orders");
                registry.Register<OrderActor>(orderRef);
            });
    }
}

// Usage in Program.cs
builder.Services.AddAkka("MySystem", (builder, sp) =>
{
    builder
        .AddOrderActors()
        .AddInventoryActors()
        .AddNotificationActors();
});

生命周期管理:

Lifetime Use When Examples
Singleton 无状态、线程安全、创建开销大 配置、HttpClient 工厂、缓存
Scoped 每请求有状态、数据库上下文 DbContext、仓储、用户上下文
Transient 轻量、有状态、创建开销小 校验器、短生命周期辅助类
// SINGLETON: Stateless services, shared safely
services.AddSingleton<IMjmlTemplateRenderer, MjmlTemplateRenderer>();

// SCOPED: Database access, per-request state
services.AddScoped<IUserRepository, UserRepository>();

// TRANSIENT: Cheap, short-lived
services.AddTransient<CreateUserRequestValidator>();

Scoped 服务需要作用域。ASP.NET Core 每个 HTTP 请求创建一个作用域;在后台服务和 actor 中需手动创建作用域。

适用场景

  • 在 ASP.NET Core 应用中组织服务注册。
  • 避免 Program.cs / Startup.cs 出现数百行注册。
  • 让服务配置在生产与测试之间复用。
  • 设计可与 Microsoft.Extensions.DependencyInjection 集成的库。
  • 使用 Akka.Hosting 配置 actor。

限制与注意事项

反模式:不要在 Program.cs 中注册所有内容;不要创建过于泛化的扩展(如 AddServices);不要隐藏重要配置(如硬编码连接字符串),应显式接受配置参数。

常见错误:单例捕获作用域服务会导致 DbContext 过期,应注入 IServiceProvider 并按操作创建作用域;后台任务缺少作用域会抛错,应使用 IServiceScopeFactory 为每个工作单元创建作用域。

// BAD: Singleton captures scoped service - stale DbContext!
public class CacheService  // Registered as Singleton
{
    private readonly IUserRepository _repo;  // Scoped - captured at startup!
}

// GOOD: Inject IServiceProvider, create scope per operation
public class CacheService
{
    private readonly IServiceProvider _serviceProvider;

    public async Task<User> GetUserAsync(string id)
    {
        using var scope = _serviceProvider.CreateScope();
        var repo = scope.ServiceProvider.GetRequiredService<IUserRepository>();
        return await repo.GetByIdAsync(id);
    }
}
// BAD: No scope for scoped services
public class BadBackgroundService : BackgroundService
{
    private readonly IOrderService _orderService;  // Scoped - will throw!
}

// GOOD: Create scope for each unit of work
public class GoodBackgroundService : BackgroundService
{
    private readonly IServiceScopeFactory _scopeFactory;

    protected override async Task ExecuteAsync(CancellationToken ct)
    {
        using var scope = _scopeFactory.CreateScope();
        var orderService = scope.ServiceProvider.GetRequiredService<IOrderService>();
        // ...
    }
}

最佳实践摘要:将相关服务分组为 Add* 方法;将扩展放在其注册的服务附近;返回 IServiceCollection 以支持链式调用;接受配置参数;使用一致命名(Add{Feature}Services);通过复用生产扩展进行测试。

参考文件 advanced-patterns.md 包含:使用 DI 扩展进行测试、Akka.NET actor 作用域管理、条件/工厂/键控注册模式。

资源:

  • Microsoft.Extensions.DependencyInjection: https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection
  • Akka.Hosting: https://github.com/akkadotnet/Akka.Hosting
  • Akka.DependencyInjection: https://getakka.net/articles/actors/dependency-injection.html
  • Options Pattern: 参见 microsoft-extensions-configuration 技能