dependency-injection-patterns
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDependency Injection Patterns
依赖注入模式
When to Use This Skill
何时使用此技巧
Use this skill when:
- Organizing service registrations in ASP.NET Core applications
- Avoiding massive Program.cs/Startup.cs files with hundreds of registrations
- Making service configuration reusable between production and tests
- Designing libraries that integrate with Microsoft.Extensions.DependencyInjection
在以下场景中使用本技巧:
- 在ASP.NET Core应用中组织服务注册
- 避免Program.cs/Startup.cs文件因数百条注册代码变得臃肿不堪
- 让服务配置可在生产环境和测试环境之间复用
- 设计与Microsoft.Extensions.DependencyIntegration集成的类库
Reference Files
参考文件
- advanced-patterns.md: Testing with DI extensions, Akka.NET actor scope management, conditional/factory/keyed registration patterns
- advanced-patterns.md:DI扩展的测试方法、Akka.NET actor作用域管理、条件/工厂/键控注册模式
The Problem
存在的问题
Without organization, Program.cs becomes unmanageable:
csharp
// BAD: 200+ lines of unorganized registrations
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.AddScoped<IProductRepository, ProductRepository>();
builder.Services.AddScoped<IUserService, UserService>();
// ... 150 more lines ...Problems: hard to find related registrations, no clear boundaries, can't reuse in tests, merge conflicts.
如果不进行组织,Program.cs会变得难以维护:
csharp
// 糟糕示例:200+行无组织的注册代码
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.AddScoped<IProductRepository, ProductRepository>();
builder.Services.AddScoped<IUserService, UserService>();
// ... 还有150多行 ...问题:难以查找相关注册、无清晰边界、无法在测试中复用、易出现合并冲突。
The Solution: Extension Method Composition
解决方案:扩展方法组合
Group related registrations into extension methods:
csharp
// GOOD: Clean, composable Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddUserServices()
.AddOrderServices()
.AddEmailServices()
.AddPaymentServices()
.AddValidators();
var app = builder.Build();将相关注册分组到扩展方法中:
csharp
// 优秀示例:简洁、可组合的Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddUserServices()
.AddOrderServices()
.AddEmailServices()
.AddPaymentServices()
.AddValidators();
var app = builder.Build();Extension Method Pattern
扩展方法模式
Basic Structure
基础结构
csharp
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;
}
}csharp
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;
}
}With Configuration
带配置的扩展方法
csharp
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;
}
}csharp
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;
}
}File Organization
文件组织方式
Place extension methods near the services they register:
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()Convention: next to the feature's services.
{Feature}ServiceCollectionExtensions.cs将扩展方法放在其注册的服务附近:
src/
MyApp.Api/
Program.cs # 组合所有Add*方法
MyApp.Users/
Services/
UserService.cs
UserServiceCollectionExtensions.cs # 包含AddUserServices()
MyApp.Orders/
OrderServiceCollectionExtensions.cs # 包含AddOrderServices()
MyApp.Email/
EmailServiceCollectionExtensions.cs # 包含AddEmailServices()约定: 文件放在对应功能的服务旁边。
{Feature}ServiceCollectionExtensions.csNaming Conventions
命名约定
| Pattern | Use For |
|---|---|
| General feature registration |
| Short form when unambiguous |
| When primarily setting options |
| Middleware (on IApplicationBuilder) |
| 模式 | 使用场景 |
|---|---|
| 通用功能注册 |
| 含义明确时的简写形式 |
| 主要用于设置选项时 |
| 中间件(基于IApplicationBuilder) |
Testing Benefits
测试优势
The pattern lets you reuse production configuration in tests and only override what's different. Works with WebApplicationFactory, Akka.Hosting.TestKit, and standalone ServiceCollection.
Add*See advanced-patterns.md for complete testing examples.
Add*模式允许你在测试中复用生产环境配置,仅覆盖需要修改的部分。适用于WebApplicationFactory、Akka.Hosting.TestKit和独立的ServiceCollection。
完整测试示例请查看 advanced-patterns.md。
Layered Extensions
分层扩展方法
For larger applications, compose extensions hierarchically:
csharp
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();
}
}对于大型应用,可以按层级组合扩展方法:
csharp
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 Integration
Akka.Hosting集成
The same pattern works for Akka.NET actor configuration:
csharp
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();
});See skill for complete Akka.Hosting patterns.
akka-hosting-actor-patterns相同模式也适用于Akka.NET actor配置:
csharp
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);
});
}
}
// 在Program.cs中的使用方式
builder.Services.AddAkka("MySystem", (builder, sp) =>
{
builder
.AddOrderActors()
.AddInventoryActors()
.AddNotificationActors();
});完整的Akka.Hosting模式请查看技巧文档。
akka-hosting-actor-patternsAnti-Patterns
反模式
Don't: Register Everything in Program.cs
不要:在Program.cs中注册所有服务
csharp
// BAD: Massive Program.cs with 200+ lines of registrationscsharp
// 糟糕示例:包含200+行注册代码的臃肿Program.csDon't: Create Overly Generic Extensions
不要:创建过于通用的扩展方法
csharp
// BAD: Too vague, doesn't communicate what's registered
public static IServiceCollection AddServices(this IServiceCollection services) { ... }csharp
// 糟糕示例:过于模糊,无法明确注册了哪些服务
public static IServiceCollection AddServices(this IServiceCollection services) { ... }Don't: Hide Important Configuration
不要:隐藏重要配置
csharp
// BAD: Buried settings
public static IServiceCollection AddDatabase(this IServiceCollection services)
{
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer("hardcoded-connection-string")); // Hidden!
}
// GOOD: Accept configuration explicitly
public static IServiceCollection AddDatabase(
this IServiceCollection services,
string connectionString)
{
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(connectionString));
}csharp
// 糟糕示例:配置被隐藏
public static IServiceCollection AddDatabase(this IServiceCollection services)
{
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer("hardcoded-connection-string")); // 配置被隐藏!
}
// 优秀示例:显式接收配置
public static IServiceCollection AddDatabase(
this IServiceCollection services,
string connectionString)
{
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(connectionString));
}Best Practices Summary
最佳实践总结
| Practice | Benefit |
|---|---|
Group related services into | Clean Program.cs, clear boundaries |
| Place extensions near the services they register | Easy to find and maintain |
Return | Fluent API |
| Accept configuration parameters | Flexibility |
Use consistent naming ( | Discoverability |
| Test by reusing production extensions | Confidence, less duplication |
| 实践方式 | 优势 |
|---|---|
| 将相关服务分组到Add*方法中 | 简洁的Program.cs、清晰的边界 |
| 将扩展方法放在对应服务附近 | 易于查找和维护 |
| 返回IServiceCollection以支持链式调用 | 流畅的API风格 |
| 接收配置参数 | 更高的灵活性 |
| 使用一致的命名(如Add{Feature}Services) | 更好的可发现性 |
| 通过复用生产环境扩展方法进行测试 | 提升信心、减少代码重复 |
Lifetime Management
生命周期管理
| Lifetime | Use When | Examples |
|---|---|---|
| Singleton | Stateless, thread-safe, expensive to create | Configuration, HttpClient factories, caches |
| Scoped | Stateful per-request, database contexts | DbContext, repositories, user context |
| Transient | Lightweight, stateful, cheap to create | Validators, short-lived helpers |
csharp
// 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 services require a scope. ASP.NET Core creates one per HTTP request. In background services and actors, create scopes manually.
See advanced-patterns.md for actor scope management patterns.
| 生命周期 | 使用场景 | 示例 |
|---|---|---|
| Singleton(单例) | 无状态、线程安全、创建成本高 | 配置、HttpClient工厂、缓存 |
| Scoped(作用域) | 每个请求有独立状态、数据库上下文 | DbContext、仓储、用户上下文 |
| Transient(瞬时) | 轻量级、有状态、创建成本低 | 验证器、短生命周期助手类 |
csharp
// SINGLETON:无状态服务,可安全共享
services.AddSingleton<IMjmlTemplateRenderer, MjmlTemplateRenderer>();
// SCOPED:数据库访问、每个请求独立状态
services.AddScoped<IUserRepository, UserRepository>();
// TRANSIENT:轻量、短生命周期
services.AddTransient<CreateUserRequestValidator>();作用域服务需要在作用域内使用。ASP.NET Core会为每个HTTP请求创建一个作用域。在后台服务和actor中,需要手动创建作用域。
actor作用域管理模式请查看 advanced-patterns.md。
Common Mistakes
常见错误
Injecting Scoped into Singleton
将作用域服务注入单例服务
csharp
// 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);
}
}csharp
// 糟糕示例:单例服务捕获作用域服务 - 导致DbContext过期!
public class CacheService // 注册为单例
{
private readonly IUserRepository _repo; // 作用域服务 - 在启动时被捕获!
}
// 优秀示例:注入IServiceProvider,每次操作创建作用域
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);
}
}No Scope in Background Work
后台任务中未创建作用域
csharp
// 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>();
// ...
}
}csharp
// 糟糕示例:未为作用域服务创建作用域
public class BadBackgroundService : BackgroundService
{
private readonly IOrderService _orderService; // 作用域服务 - 会抛出异常!
}
// 优秀示例:为每个工作单元创建作用域
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>();
// ...
}
}Resources
参考资源
- 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: See skill
microsoft-extensions-configuration
- 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
- 选项模式:查看技巧文档
microsoft-extensions-configuration