依赖注入集成原理
🔧 深入理解 MediatR 与 DI 容器的集成机制
📖 概述
MediatR 通过 ServiceFactory 委托与各种 DI 容器解耦。本章深入分析其与 Microsoft.Extensions.DependencyInjection 的集成原理,以及如何适配第三方容器。
源码位置: MediatR.Extensions.Microsoft.DependencyInjection
🏗️ AddMediatR 扩展方法内部实现
源码结构
csharp
// MediatR.Extensions.Microsoft.DependencyInjection/ServiceCollectionExtensions.cs
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddMediatR(
this IServiceCollection services,
Action<MediatRServiceConfiguration> configure)
{
// 1. 创建配置对象
var configuration = new MediatRServiceConfiguration();
configure(configuration);
// 2. 注册核心服务
RegisterCoreServices(services, configuration);
// 3. 扫描并注册 Handlers 和 Behaviors
RegisterHandlersAndBehaviors(services, configuration);
return services;
}
private static void RegisterCoreServices(
IServiceCollection services,
MediatRServiceConfiguration configuration)
{
// 注册 Mediator
services.TryAdd(new ServiceDescriptor(
typeof(IMediator),
configuration.MediatorImplementationType ?? typeof(Mediator),
configuration.Lifetime));
// 注册 ISender 和 IPublisher(指向同一个 Mediator 实例)
services.TryAdd(new ServiceDescriptor(
typeof(ISender),
sp => sp.GetRequiredService<IMediator>(),
configuration.Lifetime));
services.TryAdd(new ServiceDescriptor(
typeof(IPublisher),
sp => sp.GetRequiredService<IMediator>(),
configuration.Lifetime));
// 注册 ServiceFactory(关键!)
services.TryAdd(new ServiceDescriptor(
typeof(ServiceFactory),
sp => sp.GetService)); // ← 这就是 ServiceFactory 的实现
}
private static void RegisterHandlersAndBehaviors(
IServiceCollection services,
MediatRServiceConfiguration configuration)
{
foreach (var assemblyToScan in configuration.AssembliesToScan)
{
// 扫描 IRequestHandler
RegisterRequestHandlers(services, assemblyToScan, configuration.Lifetime);
// 扫描 INotificationHandler
RegisterNotificationHandlers(services, assemblyToScan, configuration.Lifetime);
// 扫描 IPipelineBehavior
RegisterPipelineBehaviors(services, assemblyToScan, configuration.Lifetime);
}
}
}🔍 ServiceFactory 的核心作用
ServiceFactory 定义
csharp
// MediatR/src/MediatR/Mediator.cs
public delegate object ServiceFactory(Type serviceType);本质:一个委托,接收 Type 参数,返回 object 实例。
在 ASP.NET Core 中的实现
csharp
// 注册时
services.TryAddTransient<ServiceFactory>(sp => sp.GetService);
// 等价于
services.AddTransient<ServiceFactory>(serviceProvider =>
{
return (Type serviceType) => serviceProvider.GetService(serviceType);
});工作原理:
csharp
// 使用时
var serviceFactory = serviceProvider.GetRequiredService<ServiceFactory>();
// 调用
var handler = serviceFactory(typeof(IRequestHandler<CreateOrderCommand, OrderResult>));
// 实际执行的是
var handler = serviceProvider.GetService(typeof(IRequestHandler<CreateOrderCommand, OrderResult>));为什么需要 ServiceFactory?
问题:直接依赖 DI 容器
csharp
// ❌ 错误:Mediator 直接依赖 IServiceProvider
public class Mediator : IMediator
{
private readonly IServiceProvider _serviceProvider;
public Mediator(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken ct)
{
var handler = _serviceProvider.GetService<IRequestHandler<TRequest, TResponse>>();
// ...
}
}问题:
- ❌ 耦合到特定的 DI 容器
- ❌ 无法与其他容器集成(Autofac、Unity等)
- ❌ 测试困难
解决方案:ServiceFactory 抽象
csharp
// ✅ 正确:通过委托解耦
public class Mediator : IMediator
{
private readonly ServiceFactory _serviceFactory;
public Mediator(ServiceFactory serviceFactory)
{
_serviceFactory = serviceFactory;
}
public Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken ct)
{
// 不关心具体是哪个 DI 容器
var handler = (IRequestHandler<TRequest, TResponse>)_serviceFactory(
typeof(IRequestHandler<TRequest, TResponse>));
// ...
}
}优势:
- ✅ 与 DI 容器解耦
- ✅ 可适配任何容器
- ✅ 易于测试(Mock 委托即可)
🔄 服务生命周期的影响
三种生命周期对比
1. Transient(默认)
csharp
builder.Services.AddMediatR(cfg => {
cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
cfg.Lifetime = ServiceLifetime.Transient; // 默认
});行为:
- 每次
Send()都创建新的 Mediator 实例 - 每次解析都创建新的 Handler 实例
- 管道行为也是每次新建
内存图:
Request 1: Mediator₁ → Handler₁ → Behavior₁
Request 2: Mediator₂ → Handler₂ → Behavior₂
Request 3: Mediator₃ → Handler₃ → Behavior₃优点:
- ✅ 无线程安全问题
- ✅ 状态隔离
缺点:
- ❌ GC 压力大
- ❌ 性能略低
2. Scoped(Web应用推荐)
csharp
builder.Services.AddMediatR(cfg => {
cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
cfg.Lifetime = ServiceLifetime.Scoped;
});行为:
- 每个 HTTP 请求共享同一个 Mediator 实例
- Handler 在请求范围内复用
内存图:
HTTP Request 1: Mediator₁ → Handler₁ (整个请求过程复用)
├─ GET /api/orders/1
└─ POST /api/orders
HTTP Request 2: Mediator₂ → Handler₂ (新的请求,新的实例)
└─ GET /api/orders/2优点:
- ✅ 减少实例创建
- ✅ 适合 Web 应用
- ✅ 可共享请求级别的状态
缺点:
- ⚠️ 需要注意线程安全
3. Singleton
csharp
builder.Services.AddMediatR(cfg => {
cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
cfg.Lifetime = ServiceLifetime.Singleton;
});行为:
- 整个应用只有一个 Mediator 实例
- 所有请求共享
⚠️ 警告:如果 Handler 注入了 Scoped 服务(如 DbContext),会抛出异常!
InvalidOperationException: Cannot consume scoped service 'DbContext'
from singleton 'IMediator'.解决方案:确保所有依赖都是 Singleton 或使用 Scope
📦 自动扫描与注册机制
扫描流程
csharp
private static void RegisterHandlersAndBehaviors(
IServiceCollection services,
MediatRServiceConfiguration configuration)
{
foreach (var assemblyToScan in configuration.AssembliesToScan)
{
// 1. 获取程序集中所有类型
var types = assemblyToScan.GetTypes();
// 2. 过滤出实现 IRequestHandler 的类型
var handlers = types.Where(t =>
t.GetInterfaces().Any(i =>
i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IRequestHandler<,>)));
// 3. 注册到 DI 容器
foreach (var handler in handlers)
{
var interfaces = handler.GetInterfaces()
.Where(i => i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IRequestHandler<,>));
foreach (var @interface in interfaces)
{
services.Add(new ServiceDescriptor(@interface, handler, configuration.Lifetime));
}
}
}
}注册示例
假设有以下 Handler:
csharp
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, OrderResult>
{
// ...
}
public class GetOrderHandler : IRequestHandler<GetOrderQuery, OrderDto>
{
// ...
}注册后的 DI 容器:
csharp
services.AddTransient<IRequestHandler<CreateOrderCommand, OrderResult>, CreateOrderHandler>();
services.AddTransient<IRequestHandler<GetOrderQuery, OrderDto>, GetOrderHandler>();多程序集扫描
csharp
builder.Services.AddMediatR(cfg => {
cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
cfg.RegisterServicesFromAssembly(typeof(OrderHandlersMarker).Assembly);
cfg.RegisterServicesFromAssembly(typeof(UserHandlersMarker).Assembly);
});扫描顺序:按添加顺序扫描,后扫描的同名 Handler 会覆盖前面的。
🔌 第三方容器适配
Autofac 集成
bash
dotnet add package Autofac.Extensions.DependencyInjectioncsharp
// Program.cs
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder =>
{
// 注册 MediatR
containerBuilder.RegisterMediatR(cfg => {
cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
});
});Autofac 内部实现:
csharp
// Autofac.Extensions.DependencyInjection/AutofacRegistration.cs
public static void RegisterMediatR(this ContainerBuilder builder, Action<MediatRServiceConfiguration> configure)
{
var configuration = new MediatRServiceConfiguration();
configure(configuration);
// 注册 ServiceFactory(适配 Autofac)
builder.Register<ServiceFactory>(ctx =>
{
var c = ctx.Resolve<IComponentContext>();
return t => c.Resolve(t); // ← Autofac 的解析方式
});
// 注册其他服务...
}Unity 集成
csharp
// Unity 适配器
container.RegisterType<ServiceFactory>(
new InjectionFactory(c => t => c.Resolve(t)));
container.RegisterType<IMediator, Mediator>();自定义容器适配
csharp
// 假设你有一个自定义 DI 容器
public class CustomContainer
{
public object Resolve(Type type) { /* ... */ }
}
// 适配 MediatR
var customContainer = new CustomContainer();
// 创建 ServiceFactory
ServiceFactory serviceFactory = type => customContainer.Resolve(type);
// 创建 Mediator
var mediator = new Mediator(serviceFactory);关键:只需提供 ServiceFactory 委托,无需修改 MediatR 源码!
🎯 性能优化点
1. 避免重复扫描
csharp
// ❌ 错误:每次启动都扫描
services.AddMediatR(cfg => {
cfg.RegisterServicesFromAssemblies(AppDomain.CurrentDomain.GetAssemblies());
});
// ✅ 正确:只扫描必要的程序集
services.AddMediatR(cfg => {
cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
});性能提升:启动时间减少 50-70%
2. 使用 TryAdd 避免重复注册
csharp
// MediatR 内部使用 TryAdd
services.TryAdd(new ServiceDescriptor(typeof(IMediator), typeof(Mediator), lifetime));
// 如果已经注册过,不会重复添加3. 延迟初始化
csharp
// 仅在首次使用时初始化
private static Lazy<IMediator> _mediator = new Lazy<IMediator>(() =>
{
var services = new ServiceCollection();
services.AddMediatR(cfg =>
cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));
return services.BuildServiceProvider().GetRequiredService<IMediator>();
});🐛 常见问题排查
问题 1:Handler 未被注册
症状:No service for type 'IRequestHandler'
排查:
csharp
// 调试代码
var assembly = typeof(Program).Assembly;
var types = assembly.GetTypes();
Console.WriteLine($"程序集: {assembly.FullName}");
Console.WriteLine($"总类型数: {types.Length}");
var handlers = types.Where(t =>
t.GetInterfaces().Any(i =>
i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IRequestHandler<,>)));
Console.WriteLine($"找到 {handlers.Count()} 个 Handler:");
foreach (var h in handlers)
{
Console.WriteLine($" - {h.FullName}");
}常见原因:
- Handler 不是
public - Handler 是
abstract - 程序集路径错误
问题 2:循环依赖
症状:A circular dependency was detected
原因:Handler A 依赖 Handler B,Handler B 依赖 Handler A
解决:提取共享服务
csharp
// ❌ 循环依赖
public class OrderHandler { private readonly PaymentHandler _payment; }
public class PaymentHandler { private readonly OrderHandler _order; }
// ✅ 提取服务
public class OrderProcessingService { /* ... */ }
public class OrderHandler { private readonly OrderProcessingService _service; }
public class PaymentHandler { private readonly OrderProcessingService _service; }🎓 总结
核心机制
- ✅ ServiceFactory 委托:解耦 DI 容器
- ✅ 自动扫描:反射查找 Handler 和 Behavior
- ✅ 生命周期管理:由 DI 容器控制
- ✅ TryAdd:避免重复注册
- ✅ 可扩展:轻松适配第三方容器
最佳实践
✅ Web 应用使用 Scoped
✅ 只扫描必要的程序集
✅ 避免在 Singleton 中注入 Scoped 服务
✅ 使用 TryAdd 防止重复注册
✅ 提取共享服务避免循环依赖
💡 提示: 理解 DI 集成原理有助于排查问题和优化性能!