How to keep interceptors related to each other?

Viewed 159

I created some interceptors for request logging, response logging and exception with autofac and castle. Some methods can have request logs and some response logs or both. I want to wire up requests with exceptions and responses.

I mean I want to pass request id to other interceptors. I don't want to keep all of them in one interceptor. Is there a way to get unique id from invocation or shared variable on same invocation?

Let's check code below, I want to access same id to wire before and after logs.

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");
    }
}

Thanks in advance.

1 Answers

To get a unique identifier for the actual request you can introduce a new service that will provide this identifier.

For example let's call it IContextIdentifier

public interfac IContextIdentifier
{
    String Identifer { get; }
}

And

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();
    }
}

the concrete implementation of IContextIdentifier could be as simple as :

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

Then you register the ContextIdentifier as InstancePerLifetimeScope to have a unique instance per request.

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

You can also use the HttpContext.TraceIdentifier property if you have access to an HttpContext; HttpContext

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

    private readonly IHttpContextAccessor _httpContextAccessor;

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

As far as I know there is no built-in solution to obtain the current method call identifier using Castle.Core interception framework. But you can use ConditionalWeakTable on IInvocation instance to get a unique identifier.

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;
    }
}

you should register MethodCallIdentifier as single instance

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

and then your interceptors can have a dependency on IMethodCallIdentifier<IInvocation>

I have not tried this solution, but it should work.

Related