我的项目中使用任何外部资源(数据库、Web 服务调用等)的每个方法都必须进行日志记录
我简直不敢相信。您可能记录了太多。
句子看起来一样,但它们的参数发生了变化(即方法名称)。
在这种情况下,您可能在代码中遗漏了一些通用抽象。根据我的经验,您应该:
对架构上高度相关的代码有一个通用的抽象。
例如,看看这篇关于命令处理程序的文章和这篇关于存储库和自定义查询的文章。两篇文章都围绕架构概念定义了一个通用接口。(您可能应该至少阅读第一篇文章,以了解我的其余答案)。
使用通用接口,可以很容易地用装饰器包装一整套相关操作。以这个装饰器为例,它可以包裹所有执行用例的操作:
public class LoggingCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand>
{
private readonly ICommandHandler<TCommand> decoratee;
private readonly ILogger logger;
public LoggingCommandHandlerDecorator(
ICommandHandler<TCommand> decoratee,
ILogger logger)
{
this.decoratee = decoratee;
this.logger = logger;
}
void ICommandHandler<TCommand>.Handle(TCommand command)
{
using (new AutoLog())
{
this.decoratee.Handle(command);
}
}
}
我应该如何实现我的 AutoLog 类以尽可能快地工作?
你知道吗,装饰器只是一个薄薄的包装器,几乎没有开销。使用这种方法,您通常拥有的所有方法参数现在都打包在一个对象中。这对于日志记录非常方便,因为您可以简单地将整个序列化为TCommand
XML、JSon 或更易读的格式,并且您只需编写此代码一次。
并且由于您担心性能,因为这LoggingCommandHandlerDecorator<TCommand>
是一个泛型类型,您可以做的是向该类型添加一个静态构造函数(静态构造函数在该接口的每个封闭泛型版本运行一次)并预编译一些代码,以便您有效地序列化该命令消息。绝对没有什么可以与之竞争的。甚至 AOP 框架在访问方法的参数时也使用反射并使用装箱构建动态数组。
请注意,像这样应用装饰器并没有什么“穷人的”。但是,当您的架构不遵循SOLID原则时,当您尝试这样做时,您很快就会遇到麻烦。这些原则是获得灵活且可维护的软件的关键。