我正在尝试开始利用面向方面的编程来完成重复性任务。我不确定如何分离关注点。我正在使用 C# 和 AOP 我正在使用 Castle.DynamicProxy (使用 Autofac 的 InterceptedBy 功能),但我希望这个问题的答案可以是适用于其他 AOP 解决方案的足够普遍的建议(也许你可以说服我切换到不同的 AOP 解决方案)。
例如,我有类似以下拦截器的东西,它拦截对一个类的所有方法调用。它目前有两个问题:调用方法时,(1)测量调用花费了多长时间,以及(2)在调用之前和之后记录方法的名称。
public class TimeLoggingInterceptor : IInterceptor
{
private ILog m_Log;
public TimeLoggingInterceptor(ILog log)
{
m_Log = log;
}
public void Intercept(IInvocation invocation)
{
// Logging concerns
string fullMethodName = invocation.TargetType.Name + "." + invocation.MethodInvocationTarget.Name;
m_Log.Debug(fullMethodName + " started.");
// Timing concerns
DateTime beforeStamp = DateTime.UtcNow;
// Call method
invocation.Proceed();
// Timing concerns
DateTime afterStamp = DateTime.UtcNow;
TimeSpan callTime = afterStamp - beforeStamp;
// Logging concerns
m_Log.Debug(fullMethodName + " finished. Took " + callTime.TotalMilliseconds + "ms.");
}
}
我有一种压倒性的感觉,这里的时间问题(测量方法调用花费了多长时间)应该与日志记录问题(写入日志文件)分开,因为......嗯,它们是独立的问题。我正在考虑做这样的事情,但我不确定如何进行排序或将callTime变量放在哪里:
public class TimingInterceptor : IInterceptor
{
private static ThreadLocal<TimeSpan> callTime = new ThreadLocal<TimeSpan>();
public static TimeSpan CallTime
{
get
{
if (!callTime.IsValueCreated) throw new InvalidOperationException("callTime was never set");
return callTime.Value;
}
}
public void Intercept(IInvocation invocation)
{
// Timing concerns
DateTime beforeStamp = DateTime.UtcNow;
// Call method
invocation.Proceed();
// Timing concerns
DateTime afterStamp = DateTime.UtcNow;
callTime.Value = afterStamp - beforeStamp;
}
}
public class LoggingInterceptor : IInterceptor
{
private ILog m_Log;
public LoggingInterceptor(ILog log)
{
m_Log = log;
}
public void Intercept(IInvocation invocation)
{
// Logging concerns
string fullMethodName = invocation.TargetType.Name + "." + invocation.MethodInvocationTarget.Name;
m_Log.Debug(fullMethodName + " started.");
// Call method
invocation.Proceed();
// Logging concerns
m_Log.Debug(fullMethodName + " finished. Took " + TimingInterceptor.CallTime.TotalMilliseconds + "ms.");
}
}
基本上我认为这里需要发生的是,不知何故,TimingInterceptor需要直接拦截方法,然后LoggingInterceptor需要环绕它。
人们使用什么方法来确保这些问题会以正确的顺序发生?我是否链接拦截器,让 LoggingInterceptor 拦截 TimingInterceptor 的 Intercept 方法?[InterceptOrder(1|2|3|...)]
或者我是否在拦截器类上添加了某种属性?或者我可以[InterceptAfter(typeof(TimingInterceptor))]
在 LoggingInterceptor 上放置类似的东西吗?
除了使用线程局部变量来跟踪调用时间,还有更好的选择吗?是的,我希望这是线程安全的。我在想将这个变量保留在堆栈上可能是首选,但我不确定 LoggingInterceptor 如何在不引入过多耦合的情况下获得 TimingInterceptor 的句柄(如果能够在不重新编译 LoggingInterceptor 的情况下切换 TimingInterceptor 实现会很好) .