dependency-injection-patterns by aaronontheweb/dotnet-skills
npx skills add https://github.com/aaronontheweb/dotnet-skills --skill dependency-injection-patterns在以下情况下使用此技能:
如果不进行组织,Program.cs 将变得难以管理:
// 不良做法: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 多行 ...
问题:难以找到相关注册、没有清晰的边界、无法在测试中重用、合并冲突。
将相关注册分组到扩展方法中:
// 良好做法:清晰、可组合的 Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddUserServices()
.AddOrderServices()
.AddEmailServices()
.AddPaymentServices()
.AddValidators();
var app = builder.Build();
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
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;
}
}
将扩展方法放在它们注册的服务附近:
src/
MyApp.Api/
Program.cs # 组合所有 Add* 方法
MyApp.Users/
Services/
UserService.cs
UserServiceCollectionExtensions.cs # AddUserServices()
MyApp.Orders/
OrderServiceCollectionExtensions.cs # AddOrderServices()
MyApp.Email/
EmailServiceCollectionExtensions.cs # AddEmailServices()
约定:{功能}ServiceCollectionExtensions.cs 放在功能服务旁边。
| 模式 | 用途 |
|---|---|
Add{功能}Services() | 通用功能注册 |
Add{功能}() | 无歧义时的简短形式 |
Configure{功能}() | 主要用于设置选项时 |
Use{功能}() | 中间件(在 IApplicationBuilder 上) |
Add* 模式允许您在测试中重用生产配置,并且只覆盖不同的部分。适用于 WebApplicationFactory、Akka.Hosting.TestKit 和独立的 ServiceCollection。
有关完整的测试示例,请参阅 advanced-patterns.md。
对于大型应用程序,分层组合扩展:
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.NET 参与者配置:
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-patterns 技能。
// 不良做法:包含 200 多行注册代码的庞大 Program.cs
// 不良做法:过于模糊,没有传达注册了什么
public static IServiceCollection AddServices(this IServiceCollection services) { ... }
// 不良做法:隐藏的设置
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));
}
| 实践 | 优势 |
|---|---|
将相关服务分组到 Add* 方法中 | 干净的 Program.cs,清晰的边界 |
| 将扩展放在它们注册的服务附近 | 易于查找和维护 |
返回 IServiceCollection 以支持链式调用 | 流畅的 API |
| 接受配置参数 | 灵活性 |
使用一致的命名(Add{功能}Services) | 可发现性 |
| 通过重用生产扩展进行测试 | 信心,减少重复 |
| 生命周期 | 使用时机 | 示例 |
|---|---|---|
| 单例 | 无状态、线程安全、创建成本高 | 配置、HttpClient 工厂、缓存 |
| 作用域 | 每个请求有状态、数据库上下文 | DbContext、存储库、用户上下文 |
| 瞬态 | 轻量级、有状态、创建成本低 | 验证器、短期辅助类 |
// 单例:无状态服务,安全共享
services.AddSingleton<IMjmlTemplateRenderer, MjmlTemplateRenderer>();
// 作用域:数据库访问,每个请求的状态
services.AddScoped<IUserRepository, UserRepository>();
// 瞬态:成本低,生命周期短
services.AddTransient<CreateUserRequestValidator>();
作用域服务需要作用域。 ASP.NET Core 为每个 HTTP 请求创建一个作用域。在后台服务和参与者中,需要手动创建作用域。
有关参与者作用域管理模式,请参阅 advanced-patterns.md。
// 不良做法:单例捕获作用域服务 - 过时的 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);
}
}
// 不良做法:没有为作用域服务创建作用域
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>();
// ...
}
}
microsoft-extensions-configuration 技能每周安装量
103
仓库
GitHub 星标数
488
首次出现时间
2026 年 1 月 28 日
安全审计
安装于
claude-code72
codex69
github-copilot68
opencode68
gemini-cli65
kimi-cli62
Use this skill when:
Without organization, Program.cs becomes unmanageable:
// 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.
Group related registrations into extension methods:
// GOOD: Clean, composable Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddUserServices()
.AddOrderServices()
.AddEmailServices()
.AddPaymentServices()
.AddValidators();
var app = builder.Build();
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;
}
}
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 : {Feature}ServiceCollectionExtensions.cs next to the feature's services.
| Pattern | Use For |
|---|---|
Add{Feature}Services() | General feature registration |
Add{Feature}() | Short form when unambiguous |
Configure{Feature}() | When primarily setting options |
Use{Feature}() | Middleware (on IApplicationBuilder) |
The Add* pattern lets you reuse production configuration in tests and only override what's different. Works with WebApplicationFactory, Akka.Hosting.TestKit, and standalone ServiceCollection.
See advanced-patterns.md for complete testing examples.
For larger applications, compose extensions hierarchically:
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();
}
}
The same pattern works for Akka.NET actor configuration:
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 akka-hosting-actor-patterns skill for complete Akka.Hosting patterns.
// BAD: Massive Program.cs with 200+ lines of registrations
// BAD: Too vague, doesn't communicate what's registered
public static IServiceCollection AddServices(this IServiceCollection services) { ... }
// 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));
}
| Practice | Benefit |
|---|---|
Group related services into Add* methods | Clean Program.cs, clear boundaries |
| Place extensions near the services they register | Easy to find and maintain |
Return IServiceCollection for chaining | Fluent API |
| Accept configuration parameters | Flexibility |
Use consistent naming (Add{Feature}Services) | Discoverability |
| Test by reusing production extensions | Confidence, less duplication |
| 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 |
// 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.
// 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>();
// ...
}
}
microsoft-extensions-configuration skillWeekly Installs
103
Repository
GitHub Stars
488
First Seen
Jan 28, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
claude-code72
codex69
github-copilot68
opencode68
gemini-cli65
kimi-cli62
高级代码调试专家指南:系统化诊断与解决编程问题 | 调试流程与命令
284 周安装
agent-browser 浏览器自动化工具 - 快速网页交互与测试命令行工具
134 周安装
find-skills技能:AI智能体技能搜索与安装工具,扩展Claude能力
134 周安装
Azure Functions 最佳实践指南:独立工作进程、Node.js/Python 编程模型与反模式详解
134 周安装
gentle-teaching 温和教学框架:AI辅助学习指南,培养独立解决问题能力
134 周安装
Symfony Scheduler 异步任务调度器:实现稳定重试与失败传输的工作流
134 周安装
化学品安全与毒理学评估工具 | AI预测毒理学、ADMET分析、基因互作与监管安全数据整合
134 周安装