MediatR 技术报告
一、定义与核心概念
MediatR 是一个基于 .NET 平台的轻量级开源库,它实现了中介者模式。其核心思想是提供一个中介对象(IMediator),作为不同组件(如命令处理器、查询处理器、事件处理器)之间通信的桥梁。组件之间不直接相互调用,而是通过向中介者发送消息(可以是命令、查询或通知/事件)来请求操作或传递信息。中介者负责将消息路由到正确的处理程序(IRequestHandler<TRequest, TResponse> 或 INotificationHandler<TNotification>)执行。
其核心价值在于:
- 解耦:发送消息的组件(如控制器、服务层)无需知道消息具体由谁处理、如何处理。
- 简化架构:尤其在遵循 CQRS (命令查询职责分离) 模式或需要处理领域事件的系统中,能显著减少直接依赖关系,使代码更清晰、更易于维护和测试。
- 单一职责:每个处理程序专注于处理单一类型的消息。
二、行业应用案例
MediatR 在以下类型的项目中被广泛采用:
- Web API (ASP.NET Core):
- 场景:控制器接收到 HTTP 请求后,不再直接调用业务逻辑服务,而是通过
IMediator发送一个命令(如CreateOrderCommand)或查询(如GetProductByIdQuery)。 - 效果:控制器变得非常“瘦”,只负责接收请求、发送消息、返回响应。业务逻辑完全封装在处理程序中,提高了可测试性。
- 场景:控制器接收到 HTTP 请求后,不再直接调用业务逻辑服务,而是通过
- 领域驱动设计 (DDD):
- 场景:在聚合根内部发生状态变更后,需要通知其他聚合或服务(如订单创建后通知库存模块)。聚合根可以发布一个领域事件(如
OrderCreatedEvent),由IMediator分发给多个事件处理器。 - 效果:实现了聚合之间的松散耦合,事件处理器可以异步执行,提升响应速度。
- 场景:在聚合根内部发生状态变更后,需要通知其他聚合或服务(如订单创建后通知库存模块)。聚合根可以发布一个领域事件(如
- 微服务架构:
- 场景:虽然微服务间通信通常通过网络,但在单个微服务内部,MediatR 用于解耦模块或组件间的交互。
- 后台任务/工作流:
- 场景:处理复杂的、多步骤的业务流程。可以将流程拆分为多个命令/事件,由不同的处理器依次或并行处理。
三、应用场景
- 命令处理:执行一个操作,可能会改变系统状态(如创建、更新、删除)。通常不返回数据,或只返回操作结果(成功/失败)。
public class CreateUserCommand : IRequest<bool> { public string Username { get; set; } public string Email { get; set; } } - 查询处理:请求数据,不改变系统状态。返回查询结果。
public class GetUserByIdQuery : IRequest<UserDto> { public int UserId { get; set; } } - 事件/通知处理:广播一个事件已经发生的信息。一个事件可以有零个、一个或多个处理器(支持发布/订阅)。处理器通常执行一些副作用操作。
public class UserCreatedEvent : INotification { public int UserId { get; set; } public string Username { get; set; } }
四、学习曲线
- 入门简单:MediatR 的 API 非常简洁,核心接口只有
IMediator和几个处理程序接口 (IRequestHandler,INotificationHandler)。理解中介者模式的基本概念后,上手使用基本功能相对容易。 - 概念理解关键:掌握命令、查询、事件的概念及其使用场景是有效运用 MediatR 的基础。理解 CQRS 模式对深入应用有帮助。
- 依赖注入配置:需要在 DI 容器中注册 MediatR 及其扫描到的处理程序。通常使用
services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));。 - 进阶特性:处理管道行为 (
IPipelineBehavior) 允许在请求处理前后插入逻辑(如日志、验证、事务、性能监控),这是 MediatR 强大且灵活的地方,但理解和使用它需要更深入的知识。 - 测试:由于处理程序是独立的、依赖注入的,单元测试变得非常容易。测试发送消息的组件时,可以 Mock
IMediator。 - 潜在复杂度:如果过度使用或在大型项目中组织不当,大量分散的处理程序可能会使查找特定逻辑变得困难。合理的项目结构和命名约定很重要。
总结:对于熟悉 .NET 和 DI 的开发者,基础使用学习曲线平缓。要发挥其最大威力(如管道行为、复杂事件处理),需要一定的进阶学习。
五、示例代码
以下是一个简单的 ASP.NET Core 控制器使用 MediatR 处理命令和查询的示例:
- 定义命令和处理器 (创建用户):
// 命令定义
public class CreateUserCommand : IRequest<int> // 返回新用户的ID
{
public string Username { get; set; }
public string Email { get; set; }
}
// 命令处理器
public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, int>
{
private readonly AppDbContext _context;
public CreateUserCommandHandler(AppDbContext context)
{
_context = context;
}
public async Task<int> Handle(CreateUserCommand request, CancellationToken cancellationToken)
{
var user = new User { Username = request.Username, Email = request.Email };
_context.Users.Add(user);
await _context.SaveChangesAsync(cancellationToken);
return user.Id; // 返回新用户ID
}
}
- 定义查询和处理器 (根据ID获取用户):
// 查询定义
public class GetUserByIdQuery : IRequest<UserDto>
{
public int UserId { get; set; }
}
// 查询结果DTO
public class UserDto
{
public int Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
}
// 查询处理器
public class GetUserByIdQueryHandler : IRequestHandler<GetUserByIdQuery, UserDto>
{
private readonly AppDbContext _context;
public GetUserByIdQueryHandler(AppDbContext context)
{
_context = context;
}
public async Task<UserDto> Handle(GetUserByIdQuery request, CancellationToken cancellationToken)
{
var user = await _context.Users.FindAsync(request.UserId);
if (user == null) return null;
return new UserDto { Id = user.Id, Username = user.Username, Email = user.Email };
}
}
- 在控制器中使用 MediatR:
[ApiController]
[Route("[controller]")]
public class UsersController : ControllerBase
{
private readonly IMediator _mediator;
public UsersController(IMediator mediator)
{
_mediator = mediator; // 通过DI注入IMediator
}
[HttpPost]
public async Task<IActionResult> CreateUser([FromBody] CreateUserCommand command)
{
var userId = await _mediator.Send(command); // 发送命令
return CreatedAtAction(nameof(GetUser), new { id = userId }, null);
}
[HttpGet("{id}")]
public async Task<ActionResult<UserDto>> GetUser(int id)
{
var query = new GetUserByIdQuery { UserId = id };
var user = await _mediator.Send(query); // 发送查询
if (user == null) return NotFound();
return user;
}
}
- (可选) 事件发布和处理:
// 事件定义
public class UserCreatedEvent : INotification
{
public int UserId { get; set; }
public string Username { get; set; }
}
// 事件处理器 (示例1:发送欢迎邮件)
public class SendWelcomeEmailHandler : INotificationHandler<UserCreatedEvent>
{
public async Task Handle(UserCreatedEvent notification, CancellationToken cancellationToken)
{
// 模拟发送邮件逻辑
Console.WriteLine($"Sending welcome email to {notification.Username} (ID: {notification.UserId})...");
await Task.Delay(1000); // 模拟异步操作
}
}
// 事件处理器 (示例2:记录日志)
public class LogUserCreationHandler : INotificationHandler<UserCreatedEvent>
{
private readonly ILogger<LogUserCreationHandler> _logger;
public LogUserCreationHandler(ILogger<LogUserCreationHandler> logger)
{
_logger = logger;
}
public Task Handle(UserCreatedEvent notification, CancellationToken cancellationToken)
{
_logger.LogInformation($"New user created: {notification.Username} (ID: {notification.UserId})");
return Task.CompletedTask;
}
}
// 在命令处理器中发布事件 (修改CreateUserCommandHandler)
public async Task<int> Handle(CreateUserCommand request, CancellationToken cancellationToken)
{
var user = new User { Username = request.Username, Email = request.Email };
_context.Users.Add(user);
await _context.SaveChangesAsync(cancellationToken);
// 用户创建成功后,发布领域事件
await _mediator.Publish(new UserCreatedEvent { UserId = user.Id, Username = user.Username });
return user.Id;
}
总结:MediatR 是一个强大的工具,通过实现中介者模式,为 .NET 应用程序提供了清晰的消息传递机制。它特别适用于需要解耦、遵循 CQRS 或处理领域事件的场景。虽然基础使用简单,但充分利用其管道行为等高级特性需要一定的学习投入。正确使用 MediatR 可以显著提升代码的可维护性、可测试性和架构清晰度。
MediatR在.NET中的应用解析
789

被折叠的 条评论
为什么被折叠?



