如何保持拦截器相互关联?

How to keep interceptors related to each other?

我使用 autofac 和 castle 创建了一些用于请求记录、响应记录和异常的拦截器。一些方法可以有请求日志和一些响应日志或两者都有。我想将请求与异常和响应联系起来。

我的意思是我想将请求id传递给其他拦截器。我不想将所有这些都放在一个拦截器中。有没有办法在同一调用中从调用或共享变量中获取唯一 ID?

让我们检查下面的代码,我想在日志之前和之后访问相同的 id 来连接。

public class BeforeAspect : IInterceptor
{
    public BeforeAspect()
    {

    }
    public void Intercept(IInvocation invocation)
    {
        System.Diagnostics.Debug.WriteLine("some id on before");

        invocation.Proceed();
    }
}
public class AfterAspect : IInterceptor
{
    public AfterAspect()
    {

    }
    public void Intercept(IInvocation invocation)
    {
        invocation.Proceed();

        System.Diagnostics.Debug.WriteLine("exact id like before");
    }
}

提前致谢。

要为实际请求获取唯一标识符,您可以引入一个将提供此标识符的新服务。

例如我们称它为IContextIdentifier

public interfac IContextIdentifier
{
    String Identifer { get; }
}

public class BeforeAspect : IInterceptor
{
    public BeforeAspect(IContextIdentifier contextIdentifier)
    {
        this._contextIdentifier = contextIdentifier; 
    }

    private readonly IContextIdentifier _contextIdentifier; 


    public void Intercept(IInvocation invocation)
    {
        System.Diagnostics.Debug.WriteLine($"id is {this._contextIdentifier.Identifier}");

        invocation.Proceed();
    }
}

IContextIdentifier的具体实现可以这么简单:

public class SimpleContextIdentifier : IContextIdentifier 
{
    public SimpleContextIdentifier()
    {
        this.Identifier = Guid.NewGuid().ToString(); 
    }
    public String Identifier { get; private set; }
}

然后您将 ContextIdentifier 注册为 InstancePerLifetimeScope 以便每个请求都有一个唯一的实例。

builder.RegisterType<SimpleContextIdentifier>()
       .As<IContextIDentifier>()
       .InstancePerLifetimeScope();

如果您有权访问 HttpContext,也可以使用 HttpContext.TraceIdentifier 属性; HttpContext

public class HttpContextIdentifier : IContextIdentifier 
{
    public SimpleContextIdentifier(IHttpContextAccessor httpContextAccessor)
    {
        this._httpContextAccessor = httpContextAccessor; 
    }

    private readonly IHttpContextAccessor _httpContextAccessor;

    public String Identifier => this._httpContextAccsor.TraceIfentifier; 
}

据我所知,没有内置解决方案可以使用Castle.Core拦截框架获取当前方法调用标识符。但是您可以在 IInvocation 实例上使用 ConditionalWeakTable 来获取唯一标识符。

public class MethodCallIdentifier<T> : IMethodCallIdentifier
    where T : class
{

    private class Key
    {
        public Key()
        {
            this.Id = Guid.NewGuid();
        }
        public Guid Id { get; private set; }
    }

    public MethodCallIdentifier()
    {
        this._identifiers = new ConditionalWeakTable<T, Key>();
    }

    private readonly ConditionalWeakTable<T, Key> _identifiers;

    public Guid GetUniqueIdentifier(T obj)
    {
        Key key = this._identifiers.GetOrCreateValue(obj);
        return key.Id;
    }
}

您应该将 MethodCallIdentifier 注册为单个实例

builder.RegisterGeneric(typeof(MethodCallIdentifier<>))
       .As(typeof(IMethodCallIdentifier<>))
       .SingleInstance;

然后你的拦截器可以依赖于 IMethodCallIdentifier<IInvocation>

我没有尝试过这个解决方案,但它应该有效。