代碼解耦之道:MediatR在.NET中的實戰應用
1. 引言
MediatR是一個輕量級的中介者模式實現庫,用于在.NET應用程序中處理進程內消息傳遞。它有助于降低代碼耦合度,提高可維護性和可測試性。本文將深入探討MediatR的使用方法,并提供多個實際示例。
2. MediatR的核心概念
在深入示例之前,讓我們先了解MediatR的幾個核心概念:
- 請求(Request): 表示要執行的操作。
- 處理程序(Handler): 負責處理特定類型的請求。
- 中介者(Mediator): 協調請求和處理程序之間的通信。
3. 安裝MediatR
首先,通過NuGet包管理器安裝MediatR:
dotnet add package MediatR
4. 基本用法示例
4.1 創建請求和處理程序
讓我們從一個簡單的示例開始,創建一個獲取用戶信息的請求:
public class GetUserQuery : IRequest<UserDto>
{
public int UserId { get; set; }
}
public class GetUserQueryHandler : IRequestHandler<GetUserQuery, UserDto>
{
public async Task<UserDto> Handle(GetUserQuery request, CancellationToken cancellationToken)
{
// 模擬從數據庫獲取用戶
await Task.Delay(100, cancellationToken); // 模擬異步操作
return new UserDto { Id = request.UserId, Name = $"User {request.UserId}" };
}
}
public class UserDto
{
public int Id { get; set; }
public string Name { get; set; }
}
4.2 配置依賴注入
在Program.cs中配置MediatR:
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));
4.3 使用MediatR發送請求
在控制器或服務中使用MediatR:
public class UserController : ControllerBase
{
private readonly IMediator _mediator;
public UserController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet("{id}")]
public async Task<ActionResult<UserDto>> GetUser(int id)
{
var query = new GetUserQuery { UserId = id };
var result = await _mediator.Send(query);
return Ok(result);
}
}
5. 高級用法示例
5.1 使用通知(Notification)
通知允許多個處理程序響應同一個事件:
public class UserCreatedNotification : INotification
{
public int UserId { get; set; }
public string UserName { get; set; }
}
public class EmailNotificationHandler : INotificationHandler<UserCreatedNotification>
{
public Task Handle(UserCreatedNotification notification, CancellationToken cancellationToken)
{
Console.WriteLine($"Sending email for new user: {notification.UserName}");
return Task.CompletedTask;
}
}
public class LogNotificationHandler : INotificationHandler<UserCreatedNotification>
{
public Task Handle(UserCreatedNotification notification, CancellationToken cancellationToken)
{
Console.WriteLine($"Logging new user creation: {notification.UserId}");
return Task.CompletedTask;
}
}
使用通知:
await _mediator.Publish(new UserCreatedNotification { UserId = 1, UserName = "John Doe" });
5.2 使用管道行為(Pipeline Behavior)
管道行為允許你在處理請求之前和之后執行代碼:
public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
private readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger;
public LoggingBehavior(ILogger<LoggingBehavior<TRequest, TResponse>> logger)
{
_logger = logger;
}
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next,CancellationToken cancellationToken)
{
_logger.LogInformation($"開始處理請求 {typeof(TRequest).Name}");
var response = await next();
_logger.LogInformation($"完成處理請求 {typeof(TResponse).Name}");
return response;
}
}
注冊管道行為:
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
觸發:
var query = new GetUserQuery { UserId = 1 };
var result = await _mediator.Send(query);
return Ok();
6. 實現請求重試機制
創建一個行為來自動重試失敗的請求:
public class RetryBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly ILogger<RetryBehavior<TRequest, TResponse>> _logger;
private const int MaxRetries = 3;
public RetryBehavior(ILogger<RetryBehavior<TRequest, TResponse>> logger)
{
_logger = logger;
}
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
{
for (int i = 0; i <= MaxRetries; i++)
{
try
{
if (i > 0)
{
_logger.LogWarning($"Retrying {typeof(TRequest).Name} (attempt {i})");
}
return await next();
}
catch (Exception ex) when (i < MaxRetries)
{
_logger.LogError(ex, $"Error handling {typeof(TRequest).Name}. Retry attempt {i + 1} of {MaxRetries}");
await Task.Delay(1000 * i, cancellationToken);
}
}
throw new Exception($"Failed to process {typeof(TRequest).Name} after {MaxRetries} retries.");
}
}
// 在Program.cs中注冊
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RetryBehavior<,>));
7. 最佳實踐
- 保持請求和處理程序的簡單性和單一職責。
- 使用通知處理跨切面關注點,如日志記錄和事件發布。
- 利用管道行為實現橫切關注點,如驗證、日志記錄和性能監控。
- 在大型項目中,考慮按功能或模塊組織請求和處理程序。
- 使用依賴注入容器管理MediatR和其他依賴項。
8. 結論
MediatR是一個強大的工具,可以幫助你構建松耦合、可維護的C#應用程序。通過分離請求和處理程序,它促進了關注點分離,并使得添加新功能和修改現有功能變得更加容易。結合CQRS模式,MediatR可以成為構建可擴展和高性能應用程序的有力工具。