服务模块化注入.让服务注入拥有清晰的逻辑和结构
$ dotnet add package EasilyNET.AutoDependencyInjection一个功能强大的自动依赖注入模块系统,提供模块化的服务配置和中间件管理能力。
AppModule 的模块系统,支持依赖关系声明 (DependsOn)GetEnable 方法动态控制模块启用/禁用(可从配置文件读取)ConfigureServices(context) - 同步方法,用于服务注册(99%场景)ConfigureServicesAsync(context, ct) - 异步方法,用于罕见的异步初始化场景ApplicationInitializationSync(context) - 同步方法,用于同步的中间件/应用配置,在异步版本之前调用ApplicationInitialization(context) - 异步方法,用于中间件/应用配置ApplicationShutdown(context) - 异步方法,应用停止时按模块逆序调用,用于资源清理DependencyInjectionAttribute 中使用 ServiceKey 属性标识服务键值ResolveKeyed<T>(key) 解析键控服务Microsoft.Extensions.DependencyInjectionOwned<T> 受控生命周期管理IIndex<TKey, TService> 键控服务索引Func<TParam, TService>、Func<T1, T2, ..., TService> 最多 4 参数强类型,以及通用 支持 5+ 参数)Func<object[], TService>IModuleDiagnostics 接口| 平台 | 示例项目 | 状态 |
|---|---|---|
| WPF | WPF 示例 | ✅ 最新 |
| WinForms | WinForms 示例 | ✅ 最新 |
| WinUI3 | WinUI3 示例 | ⚠️ 待更新 |
IResolver 提供比原生 IServiceProvider 更强大的服务解析能力。
| 方法 | 说明 |
|---|---|
Resolve<T>(params Parameter[]) | 解析服务(支持参数覆盖),失败抛异常 |
ResolveKeyed<T>(key, params?) | 解析键控服务(KeyedService) |
ResolveNamed<T>(name, params?) | 解析命名服务 |
支持四种参数类型:
// 1. 基本解析
using var resolver = provider.CreateResolver();
var service = resolver.Resolve<IMyService>();
// 2. 带参数覆盖的解析
var service = resolver.Resolve<IMyService>(
new NamedParameter("connectionString", "Server=localhost"),
new TypedParameter(typeof(ILogger), logger)
);
// 3. 键控服务解析
var keyedService = resolver.ResolveKeyed<ICache>("redis",
new NamedParameter("endpoint", "127.0.0.1:6379")
);
// 4. 命名服务解析
var namedService = resolver.ResolveNamed<ICache>("primary");
// 5. 受控生命周期(Owned<T>)
var owned = provider.ResolveOwned<IScopedService>();
// 使用完毕后释放作用域
owned.Dispose();
// 6. 创建独立作用域的 Resolver
using var scopedResolver = provider.CreateResolver(createScope: true);
var scopedService = scopedResolver.Resolve<IScopedService>();也可以直接在 IServiceProvider 上使用这些能力:
// 创建 Resolver(可选择是否创建作用域)
var resolver = provider.CreateResolver(createScope: true);
// 或者直接使用扩展方法
var service = provider.Resolve<IMyService>();
var keyed = provider.ResolveKeyed<ICache>("redis");
var withParams = provider.Resolve<MyService>(
new NamedParameter("config", configuration)
);支持将参数化工厂注册为 Func 委托,注入后可在运行时传参创建服务实例:
// 1-4 参数:强类型重载
services.AddParameterizedFactory<string, IFooService>(); // Func<string, IFoo>
services.AddParameterizedFactory<string, int, IBarService>(); // Func<string, int, IBar>
services.AddParameterizedFactory<string, int, bool, IBazService>(); // Func<string, int, bool, IBaz>
services.AddParameterizedFactory<string, int, bool, string, IQuxService>(); // Func<string, int, bool, string, IQux>
// 5+ 参数:通用兜底(Func<object[], TService>),需声明参数类型用于运行时校验
services.AddParameterizedFactory<IComplexService>(
typeof(string), typeof(int), typeof(bool), typeof(string), typeof(double), typeof(string));
// 使用示例
public class MyController(
Func<string, IFooService> fooFactory,
Func<object[], IComplexService> complexFactory)
{
public void Do()
{
var foo = fooFactory("hello");
var complex = complexFactory(["a", 1, false, "b", 2.5, "c"]);
}
}
// Owned 工厂:每次调用创建独立作用域
services.AddOwnedFactory<IScopedService>();
// 注入 Func<Owned<IScopedService>>,调用后需手动 Dispose注意:多参数工厂内部使用
PositionalParameter按位置匹配,即使多个参数类型相同也能正确区分。
[FromKeyedServices] 特性注入键控依赖1. 修改 App.xaml.cs
public partial class App : Application
{
[STAThread]
public static void Main(string[] args)
{
using var host = CreateHostBuilder(args).Build();
host.InitializeApplication();
host.Start();
var app = new App();
app.InitializeComponent();
app.MainWindow = host.Services.GetRequiredService<MainWindow>();
app.MainWindow.Visibility = Visibility.Visible;
app.Run();
}
private static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureServices(sc =>
{
sc.AddApplicationModules<AppServiceModules>();
});
}
}2. 调整 .csproj 文件
<ItemGroup>
<ApplicationDefinition Remove="App.xaml" />
<Page Include="App.xaml" />
</ItemGroup>3. 创建模块类 (AppServiceModules.cs)
[DependsOn(typeof(DependencyAppModule))]
internal sealed class AppServiceModules : AppModule
{
// 同步服务注册(推荐)
public override void ConfigureServices(ConfigureServicesContext context)
{
// 注册应用服务
context.Services.AddSingleton<IMyService, MyService>();
}
// 可选:异步初始化
public override Task ConfigureServicesAsync(ConfigureServicesContext context, CancellationToken ct)
{
// 罕见的异步初始化场景
return Task.CompletedTask;
}
}4. 注册窗口和服务
// 使用特性注册窗口(注意需要 AddSelf = true)
[DependencyInjection(ServiceLifetime.Singleton, AddSelf = true, SelfOnly = true)]
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}AddSelf 必须设置为 true
GetRequiredService<MainWindow>() 获取AddSelf = true, SelfOnly = true 确保注册具体的窗口类型获取 IHost 的方式不同
// Web 项目
var app = context.GetApplicationHost() as WebApplication;
// 或
var app = context.GetApplicationHost() as IApplicationBuilder;
// 桌面项目(WPF/WinForms)
var host = context.GetApplicationHost() as IHost;
1. 使用特性注入服务
// 标记服务类,自动注入到容器
[DependencyInjection(ServiceLifetime.Scoped)]
public class OrderService : IOrderService
{
private readonly IRepository _repository;
public OrderService(IRepository repository)
{
_repository = repository;
}
}
// 支持 KeyedService
[DependencyInjection(ServiceLifetime.Singleton, ServiceKey = "redis")]
public class RedisCache : ICache
{
// ...
}2. 创建模块 (AppModule)
// Step 1: 创建功能模块(如 CORS 配置模块)
public class CorsModule : AppModule
{
// 可从配置文件读取是否启用此模块
public override bool GetEnable(ConfigureServicesContext context)
{
var config = context.Configuration; // 直接使用 context.Configuration
return config.GetSection("ServicesEnable").GetValue<bool>("Cors");
}
// 同步服务注册(推荐)
public override void ConfigureServices(ConfigureServicesContext context)
{
var config = context.Configuration; // 直接使用 context.Configuration
var allow = config["AllowedHosts"] ?? "*";
context.Services.AddCors(c =>
c.AddPolicy("AllowedHosts", s =>
s.WithOrigins(allow.Split(","))
.AllowAnyMethod()
.AllowAnyHeader()));
}
// 配置中间件(异步)
public override Task ApplicationInitialization(ApplicationContext context)
{
var app = context.GetApplicationHost() as IApplicationBuilder;
app?.UseCors("AllowedHosts");
return Task.CompletedTask;
}
}3. 创建根模块
// Step 2: 使用 DependsOn 声明模块依赖关系
[DependsOn(
typeof(DependencyAppModule), // 必须依赖,提供自动注入功能
typeof(CorsModule) // 自定义模块
)]
public class AppWebModule : AppModule
{
// 同步服务注册
public override void ConfigureServices(ConfigureServicesContext context)
{
context.Services.AddHttpContextAccessor();
// 其他服务注册
}
// 可选:异步初始化
public override Task ConfigureServicesAsync(ConfigureServicesContext context, CancellationToken ct)
{
// 罕见的异步初始化场景
return Task.CompletedTask;
}
// 应用初始化(异步)
public override Task ApplicationInitialization(ApplicationContext context)
{
var app = context.GetApplicationHost() as IApplicationBuilder;
app?.UseAuthorization();
// 其他中间件配置
return Task.CompletedTask;
}
}4. 在 Program.cs 中启用
var builder = WebApplication.CreateBuilder(args);
// 注册模块系统
builder.Services.AddApplicationModules<AppWebModule>();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// 初始化模块(执行所有模块的 ApplicationInitialization)
app.InitializeApplication();
// 或使用异步版本
// await app.InitializeApplicationAsync();
app.MapControllers();
app.Run();模块的 DependsOn 顺序决定了初始化顺序。被依赖的模块会先执行(使用拓扑排序算法):
[DependsOn(
typeof(DependencyAppModule), // 第 1 个初始化
typeof(DatabaseModule), // 第 2 个初始化(DependencyAppModule 完成后)
typeof(CachingModule), // 第 3 个初始化
typeof(AuthenticationModule) // 第 4 个初始化
)]
public class AppWebModule : AppModule // 最后初始化
{
// ...
}重要:如果 DatabaseModule 依赖 DependencyAppModule,则 DependencyAppModule 会先执行,然后是 DatabaseModule,无论它们在 DependsOn 中的声明顺序如何。
建议按功能领域划分模块:
// 数据库模块
public class DatabaseModule : AppModule
{
// 同步服务注册
public override void ConfigureServices(ConfigureServicesContext context)
{
var connectionString = context.Configuration
.GetConnectionString("Default");
context.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(connectionString));
}
}
// 认证模块
public class AuthenticationModule : AppModule
{
// 同步服务注册
public override void ConfigureServices(ConfigureServicesContext context)
{
context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => { /* ... */ });
}
// 异步中间件配置
public override Task ApplicationInitialization(ApplicationContext context)
{
var app = context.GetApplicationHost() as IApplicationBuilder;
app?.UseAuthentication();
app?.UseAuthorization();
return Task.CompletedTask;
}
}
// Swagger 文档模块
public class SwaggerModule : AppModule
{
public override bool GetEnable(ConfigureServicesContext context)
{
// 使用 context.Configuration 直接访问配置
return context.Configuration.GetValue<bool>("Swagger:Enabled");
}
// 同步服务注册
public override void ConfigureServices(ConfigureServicesContext context)
{
context.Services.AddSwaggerGen();
}
// 异步中间件配置
public override Task ApplicationInitialization(ApplicationContext context)
{
var app = context.GetApplicationHost() as IApplicationBuilder;
app?.UseSwagger();
app?.UseSwaggerUI();
return Task.CompletedTask;
}
}在 appsettings.json 中配置模块开关:
{
"ServicesEnable": {
"Cors": true,
"Swagger": true,
"HealthChecks": false
}
}在模块中读取配置:
public override bool GetEnable(ConfigureServicesContext context)
{
// 推荐使用 context.Configuration
return context.Configuration
.GetSection("ServicesEnable")
.GetValue<bool>("Swagger");
}| 属性 | 类型 | 说明 | 默认值 |
|---|---|---|---|
Lifetime | ServiceLifetime | 服务生命周期(Singleton/Scoped/Transient) | Scoped |
ServiceKey | object? | 键控服务的键值(KeyedService) | null |
AddSelf | bool | 是否注册实现类自身 | false |
SelfOnly | bool | 是否仅注册实现类(不注册接口) | false |
// 基础用法:注册接口
[DependencyInjection(ServiceLifetime.Scoped)]
public class UserService : IUserService
{
// 会注册 IUserService -> UserService
}
// 键控服务
[DependencyInjection(ServiceLifetime.Singleton, ServiceKey = "primary")]
public class PrimaryDatabase : IDatabase
{
// 会注册 Keyed Service: "primary" -> PrimaryDatabase
}
// 同时注册接口和实现类
[DependencyInjection(ServiceLifetime.Scoped, AddSelf = true)]
public class ProductService : IProductService
{
// 会注册两个:
// 1. IProductService -> ProductService
// 2. ProductService -> ProductService
}
// 仅注册实现类(常用于 Window/Page)
[DependencyInjection(ServiceLifetime.Singleton, AddSelf = true, SelfOnly = true)]
public partial class MainWindow : Window
{
// 仅注册 MainWindow -> MainWindow
// 不注册 Window -> MainWindow
}ConfigureServices 改为同步方法
ConfigureServices 从 async Task 改为 voidasync/await 关键字,删除 await Task.CompletedTask新增 ConfigureServicesAsync 方法
ConfigureServicesAsyncConfigureServices 之后调用配置访问方式变更
context.ServiceProvider.GetConfiguration()context.Configuration(推荐)或 context.ServiceProvider.GetRequiredService<IConfiguration>()IStartupModuleRunner.Initialize 签名变更
void Initialize()void Initialize(IServiceProvider serviceProvider)新增循环依赖检测
InvalidOperationException新增 IModuleDiagnostics 接口
IModuleDiagnostics 查看模块加载情况异步方法
ConfigureServices 和 ApplicationInitialization 改为异步Task,使用 await Task.CompletedTask 结束同步方法GetEnable 函数
Enable 属性GetEnable 方法,支持运行时动态判断IHost 统一
GetApplicationBuilder() 已弃用GetApplicationHost() 并根据平台转换类型// 推荐方式(新)
public override void ConfigureServices(ConfigureServicesContext context)
{
var config = context.Configuration;
var connectionString = config.GetConnectionString("Default");
}
// 兼容方式(旧,仍可用)
public override void ConfigureServices(ConfigureServicesContext context)
{
var config = context.ServiceProvider.GetRequiredService<IConfiguration>();
}// 方式 1: 使用 IServiceScopeFactory
var scopeFactory = provider.GetRequiredService<IServiceScopeFactory>();
using var scope = scopeFactory.CreateScope();
var service = scope.ServiceProvider.GetRequiredService<IScopedService>();
// 方式 2: 使用 Resolver
using var resolver = provider.CreateResolver(createScope: true);
var service = resolver.Resolve<IScopedService>();使用拓扑排序算法,确保依赖模块先执行:
ConfigureServices(按依赖顺序,依赖项优先)ConfigureServicesAsync(按依赖顺序)ApplicationInitializationSync(按依赖顺序,同步)ApplicationInitialization(按依赖顺序,异步)ApplicationShutdown(按依赖逆序)// 注入 IModuleDiagnostics
public class MyService
{
private readonly IModuleDiagnostics _diagnostics;
public MyService(IModuleDiagnostics diagnostics)
{
_diagnostics = diagnostics;
// 获取已加载模块
var modules = _diagnostics.GetLoadedModules();
foreach (var module in modules)
{
Console.WriteLine($"{module.Order}: {module.Name}");
}
// 验证依赖关系
var issues = _diagnostics.ValidateModuleDependencies();
if (issues.Count > 0)
{
foreach (var issue in issues)
{
Console.WriteLine($"警告: {issue}");
}
}
}
}重写 GetEnable 方法返回 false:
public override bool GetEnable(ConfigureServicesContext context) => false;检查 DependsOn 声明,确保没有循环依赖:
// ❌ 错误:循环依赖
[DependsOn(typeof(ModuleB))]
public class ModuleA : AppModule { }
[DependsOn(typeof(ModuleA))] // 循环依赖!
public class ModuleB : AppModule { }
// ✅ 正确:无循环依赖
[DependsOn(typeof(BaseModule))]
public class ModuleA : AppModule { }
[DependsOn(typeof(BaseModule))]
public class ModuleB : AppModule { }GetEnable 返回 false 禁用ConfigureServices 而非 ConfigureServicesAsync,除非确实需要异步操作