我一直在寻找针对AOP日志记录的特定解决方案。我需要拦截才能做到这样的事情:
[MyCustomLogging("someParameter")]
问题是,我在其他DI框架中看到了使这成为可能的例子。但我的项目已经在使用Autofac进行DI,我不知道与Unity混合是否是一个好主意(例如)。在Autofac.extras.dynamiclibrary2中,InterceptAttribute类被密封。
任何人都有这个问题的想法?
Ps。:我对此感到满意:
[Intercept(typeof(MyLoggingClass), "anotherParameter"]
答案 0 :(得分:11)
尽管使用属性来丰富带有元数据的类型以及 feed 横切关注的数据使用并不是很糟糕,但使用属性来标记类或方法运行通常是一些跨领域的关注。
使用您所显示的属性标记代码有一些严重的缺点:
Order
属性),但更改顺序意味着通过代码进行彻底更改以更改属性的Order
。忘记一个会导致错误。这违反了Open/closed principle。typeof(MyLoggingClass)
),这使得您的代码仍然静态地依赖于交叉关注点。用另一个替换类将再次导致您对代码库进行彻底更改,并且保持硬依赖性使得重用代码或在运行时或部署时决定是否应该应用方面更加困难。在许多情况下,您不能从代码到方面具有此依赖性,因为代码存在于基础库中,而方面特定于应用程序框架。例如,您可能具有在Web应用程序和Windows服务中运行的相同业务逻辑。在Web应用程序中运行时,您希望以不同的方式登录。换句话说,您违反了Dependency inversion principle。 但是如果您使用Reused abstraction principle使用SOLID和right application design,您会发现应用横切关注点(例如日志记录)只需要编写一个非常简单的装饰器,例如:
public class LoggingCommandHandlerDecorator<T> : ICommandHandler<T>
{
private readonly ILogger logger;
private readonly ICommandHandler<T> decoratee;
public LoggingCommandHandlerDecorator(ILogger logger, ICommandHandler<T> decoratee) {
this.logger = logger;
this.decoratee = decoratee;
}
public void Handle(T command) {
this.logger.Log("Handling {0}. Data: {1}", typeof(T).Name,
JsonConvert.SerializeObject(command));
this.decoratee.Handle(command);
}
}
如果没有适当的设计,你仍然可以使用拦截(没有属性),因为拦截允许你装饰&#39;任何似乎在代码中没有关系的类型(共享没有通用接口)。定义要拦截哪些类型以及哪些类型不麻烦,但您通常仍然可以在应用程序的一个位置定义它,因此无需在整个代码库中进行彻底更改。
侧节点。正如我所说,使用属性来描述message based patterns很好,pure metadata。例如,获取一些仅允许具有特定权限的用户运行的代码。您可以按如下方式标记该代码:
[Permission(Permissions.Crm.ManageCompanies)]
public class BlockCompany : ICommand {
public Guid CompanyId;
}
此属性不描述运行哪些方面,也不引用外部库中的任何类型(PermissionAttribute
是您可以(并且应该)自己定义的),或任何特定于AOP的类型。它只是用元数据丰富了代码。
最后,您显然想要应用一些横切关注点来检查当前用户是否具有正确的权限,但该属性并不会强迫您进入特定方向。使用上面的属性,我可以想象装饰器看起来如下:
public class PermissionCommandHandlerDecorator<T> : ICommandHandler<T>
{
private static readonly Guid requiredPermissionId =
typeof(T).GetCustomAttribute<PermissionAttribute>().PermissionId;
private readonly IUserPermissionChecker checker;
private readonly ICommandHandler<T> decoratee;
public PermissionCommandHandlerDecorator(IUserPermissionChecker checker,
ICommandHandler<T> decoratee) {
this.checker = checker;
this.decoratee = decoratee;
}
public void Handle(T command) {
this.checker.CheckPermission(requiredPermissionId);
this.decoratee.Handle(command);
}
}