问题:横切关注点无处不在
作为软件工程师,我们不断面临在应用程序中实现横切关注点的挑战,例如日志记录、缓存、验证、重试逻辑和安全性。传统方法通常会导致:
考虑这个典型的服务方法:
public async Task<Order> GetOrderAsync(int orderId)
{
_logger.LogInformation("Getting order {OrderId}", orderId); // 正在获取订单 {OrderId}
// 先检查缓存
var cacheKey = $"order_{orderId}";
if (_cache.TryGetValue(cacheKey, out Order cachedOrder))
{
_logger.LogInformation("Order {OrderId} found in cache", orderId); // 订单 {OrderId} 在缓存中找到
return cachedOrder;
}
try
{
// 验证输入
if (orderId <= 0)
throw new ArgumentException("Invalid order ID"); // 无效的订单 ID
// 业务逻辑埋没在基础设施代码中
var order = await _repository.GetOrderAsync(orderId);
// 缓存结果
_cache.Set(cacheKey, order, TimeSpan.FromMinutes(5));
_logger.LogInformation("Order {OrderId} retrieved successfully", orderId); // 订单 {OrderId} 成功获取
return order;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to get order {OrderId}", orderId); // 获取订单 {OrderId} 失败
throw;
}
}
此方法违反了单一职责原则(Single Responsibility Principle),并成为维护的噩梦。如果我们需要添加指标、安全检查或速率限制怎么办?该方法会变得越来越复杂。
DecoratR:简洁、可组合的服务装饰
DecoratR 是一个现代的 .NET 库,它通过流畅、直观的 API 将装饰器模式(Decorator pattern)引入到 Microsoft 的依赖注入(DI)容器中。它允许你以简洁、可组合的方式用横切关注点包装你的服务。
核心特性
快速开始
通过 NuGet 安装非常简单:
dotnet add package DecoratR
让我们将复杂的服务方法转化为职责分离的简洁形式:
// 为每个关注点分离的装饰器
public class LoggingDecorator : IOrderService
{
private readonly IOrderService _inner;
public LoggingDecorator(IOrderService inner) => _inner = inner;
public string GetOrder(string orderId)
{
Console.WriteLine("LoggingDecorator: 开始请求 (Starting request)"); // 翻译了日志信息
var result = _inner.GetOrder(orderId);
Console.WriteLine("LoggingDecorator: 请求完成 (Request completed)"); // 翻译了日志信息
return $"Logged({result})";
}
}
public class CacheDecorator : IOrderService
{
private readonly IOrderService _inner;
public CacheDecorator(IOrderService inner) => _inner = inner;
public string GetOrder(string orderId)
{
Console.WriteLine("CacheDecorator: 正在检查缓存 (Checking cache)"); // 翻译了日志信息
var result = _inner.GetOrder(orderId);
Console.WriteLine("CacheDecorator: 正在存入缓存 (Storing in cache)"); // 翻译了日志信息
return $"Cached({result})";
}
}
public class OrderService : IOrderService
{
private readonly IOrderRepository _repository;
public OrderService(IOrderRepository repository) => _repository = repository;
public string GetOrder(string orderId)
{
Console.WriteLine("OrderService: 正在从数据库获取 (Fetching from database)"); // 翻译了日志信息
return $"Order: {orderId}";
}
}
// 使用 DecoratR 配置
services.Decorate<IOrderService>()
.With<LoggingDecorator>()
.Then<CacheDecorator>()
.Then<OrderService>()
.Apply();
// 使用
var provider = services.BuildServiceProvider();
var orderService = provider.GetService<IOrderService>();
var result = orderService.GetOrder("123");
Console.WriteLine(result);
// 输出:
// LoggingDecorator: 开始请求 (Starting request)
// CacheDecorator: 正在检查缓存 (Checking cache)
// OrderService: 正在从数据库获取 (Fetching from database)
// CacheDecorator: 正在存入缓存 (Storing in cache)
// LoggingDecorator: 请求完成 (Request completed)
// Logged(Cached(Order: 123))
理解装饰器链
DecoratR 按照你定义的顺序应用装饰器,最后一个是你基础实现:
services.Decorate<IOrderService>()
.With<SecurityDecorator>() // 最外层 - 最先执行
.Then<LoggingDecorator>() // 中间层
.Then<CacheDecorator>() // 内层
.Then<OrderService>() // 基础实现 - 最内层
.Apply();
执行流程从外向内移动:
安全 (Security) → 日志 (Logging) → 缓存 (Cache) → 业务逻辑 (Business Logic)
高级特性
条件装饰 (Conditional Decoration) — 基于条件应用装饰器:
var isDevelopment = Environment.GetEnvironmentVariable("ENVIRONMENT") == "Development";
var enableCaching = configuration.GetValue<bool>("Features:Caching");
services.Decorate<IOrderService>()
.With<LoggingDecorator>()
.ThenIf<RetryDecorator>(isDevelopment) // 仅在开发环境
.ThenIf<CacheDecorator>(enableCaching) // 基于配置
.Then<OrderService>()
.Apply();
键控服务 (.NET 8+) — 为相同服务类型创建不同的装饰器链:
// 为不同上下文使用不同配置
services.Decorate<IOrderService>("internal") // "内部"键
.With<LoggingDecorator>()
.Then<OrderService>()
.Apply();
services.Decorate<IOrderService>("external") // "外部"键
.With<SecurityDecorator>()
.Then<RateLimitingDecorator>()
.Then<LoggingDecorator>()
.Then<OrderService>()
.Apply();
// 使用
var internalService = provider.GetRequiredKeyedService<IOrderService>("internal");
var externalService = provider.GetRequiredKeyedService<IOrderService>("external");
自定义工厂方法 (Custom Factory Methods) 对于需要自定义依赖注入的复杂场景:
services.Decorate<IOrderService>()
.With((serviceProvider, inner) =>
new MetricsDecorator(
inner,
serviceProvider.GetRequiredService<IMetrics>(),
serviceProvider.GetRequiredService<IConfiguration>()
.GetValue<string>("MetricsPrefix")))
.Then<OrderService>()
.Apply();
泛型装饰器 (Generic Decorators):
public class GenericCacheDecorator<T> : IService<T>
{
private readonly IService<T> _inner;
public GenericCacheDecorator(IService<T> inner) => _inner = inner;
public T Get(string key) => /* 缓存逻辑 (caching logic) */ _inner.Get(key);
}
services.Decorate<IService<User>>()
.With<GenericCacheDecorator<User>>()
.Then<UserService>()
.Apply();
实际应用场景
电子商务平台 (E-commerce Platform)
public class ECommerceConfiguration
{
public void ConfigureServices(IServiceCollection services, IConfiguration config)
{
var enableMetrics = config.GetValue<bool>("Features:Metrics");
var enableRetry = config.GetValue<bool>("Features:Retry");
// 订单处理流水线
services.Decorate<IOrderService>()
.With<SecurityDecorator>() // 首先认证
.Then<ValidationDecorator>() // 验证输入
.ThenIf<MetricsDecorator>(enableMetrics) // 可选的指标
.ThenIf<RetryDecorator>(enableRetry) // 可选的重试逻辑
.Then<CacheDecorator>() // 缓存近端数据
.Then<OrderService>() // 业务逻辑
.AsScoped() // 设置为作用域生命周期
.Apply();
// 支付处理 - 不同的需求
services.Decorate<IPaymentService>()
.With<AuditDecorator>() // 审计所有支付
.Then<EncryptionDecorator>() // 加密敏感数据
.Then<RateLimitingDecorator>() // 防止滥用
.Then<PaymentService>()
.Apply();
}
}
多租户 SaaS 应用程序 (Multi-tenant SaaS Application)
// 不同租户层级的不同功能集
services.Decorate<IDataService>("basic-tier") // "基础版"键
.With<LoggingDecorator>()
.Then<DataService>()
.Apply();
services.Decorate<IDataService>("premium-tier") // "高级版"键
.With<LoggingDecorator>()
.Then<CacheDecorator>()
.Then<MetricsDecorator>()
.Then<DataService>()
.Apply();
services.Decorate<IDataService>("enterprise-tier") // "企业版"键
.With<SecurityDecorator>()
.Then<AuditDecorator>()
.Then<LoggingDecorator>()
.Then<CacheDecorator>()
.Then<MetricsDecorator>()
.Then<DataService>()
.Apply();
最佳实践
装饰器顺序至关重要 考虑装饰器的逻辑流程:
services.Decorate<IService>()
.With<SecurityDecorator>() // 认证/授权最先
.Then<RateLimitingDecorator>() // 速率限制在安全之后
.Then<LoggingDecorator>() // 在安全检查之后记录日志
.Then<MetricsDecorator>() // 收集指标
.Then<CacheDecorator>() // 缓存最接近数据
.Then<BusinessService>() // 纯粹的业务逻辑
.Apply();
保持装饰器职责单一 每个装饰器应只负责单一职责:
// 好 - 单一关注点
public class CacheDecorator : IOrderService
{
// 只处理缓存逻辑
}
// 坏 - 多个关注点
public class CacheAndLogDecorator : IOrderService
{
// 同时处理缓存和日志记录 - 违反 SRP
}
使装饰器可测试 装饰器很容易独立进行单元测试:
[Test]
public void LoggingDecorator_Should_LogExecution() // LoggingDecorator 应记录执行
{
// 准备 (Arrange)
var mockInner = new Mock<IOrderService>();
var mockLogger = new Mock<ILogger>();
var decorator = new LoggingDecorator(mockInner.Object, mockLogger.Object);
// 执行 (Act)
decorator.GetOrder("123");
// 断言 (Assert)
mockLogger.Verify(x => x.LogInformation(It.IsAny<string>()), Times.AtLeastOnce); // 验证至少记录一次信息
mockInner.Verify(x => x.GetOrder("123"), Times.Once); // 验证内部服务方法被调用一次
}
明智地使用条件装饰 避免使用运行时条件装饰来降低复杂性:
// 好 - 条件在启动时仅评估一次
services.Decorate<IService>()
.WithIf<ExpensiveDecorator>(enableExpensiveFeature) // 启用昂贵特性时才应用
.Then<BaseService>()
.Apply();
// 坏 - 条件在每次调用时评估
public class ConditionalDecorator : IService
{
public Result Execute()
{
if (someRuntimeCondition) // 每次调用都评估!
{
// 昂贵的操作
}
return _inner.Execute();
}
}
性能考量
DecoratR 专为性能而设计:
从现有模式迁移
之前:
// 手动注册 - 易错且冗长
services.AddScoped<IOrderService>(provider =>
{
var baseService = new OrderService(provider.GetService<IRepository>());
var cached = new CacheDecorator(baseService, provider.GetService<ICache>());
var logged = new LoggingDecorator(cached, provider.GetService<ILogger>());
return logged;
});
之后:
// 简洁、流式的 API
services.Decorate<IOrderService>()
.With<LoggingDecorator>()
.Then<CacheDecorator>()
.Then<OrderService>()
.Apply();
平台支持
DecoratR 支持多个 .NET 版本:
键控服务仅在 .NET 8.0 及更高版本中可用,但所有其他功能在所有支持的版本中均可使用。
DecoratR 改变了你在 .NET 应用程序中处理横切关注点的方式。通过为装饰器模式提供简洁、流畅的 API,它使你能够:
无论你是在构建微服务、Web API 还是桌面应用程序,DecoratR 都能帮助你编写遵循 SOLID 原则的、更清晰、更易于维护的代码。