Search code examples
c#reflectionpostsharp

Passing a class object that only PostSharp can see


I'm not sure if this is possible or not.

I have a method call from client api to service api (two separate projects in two separate locations) that I want post sharp to intercept. Calls are from the client and postsharp is on the service

service.GetLogin(username)

The key here is I need to pass an authorization object that will show in the postsharp onentry method

 public class Authorization
    {
        public string client_id  { get; set; }
         public string client_secret { get; set; }
        public string access_token { get; set; }
        public string token_type { get; set; }
        public string expires_in { get; set; }
    }

var auth = new Authorization();
 auth.client_id = "xyz";
 auth.client_secret = "abc"

and in postsharp

 [Serializable]
    public class LoggingAspect : OnMethodBoundaryAspect
    {
        public override void OnEntry(MethodExecutionArgs args)
        {
           //I need the object to show up here
        }
    }

Problem: I do not want to pass this object in every single method call as I have over 1000 methods. Is there a way to attach that authorization object to each call so that postsharp can see it without doing something like this

service.GetLogin(username, auth);
service.Foo(auth)
service.Bar(auth);
service.xyz(auth);
services.abc(auth);

Can you imagine adding just this one object to over 1000 methods?


Solution

  • PostSharp is unable to directly consume data from call sites, as it would require adding an actual parameter to the method (or emitting a new overload). This is in theory possible with the low-level SDK, but would be hard to maintain and costly to implement.

    The best way to achieve this is to use AsyncLocal<T> which allows you to store data in the ExecutionContext.

    public class AuthorizationProvider
    {
      private static AsyncLocal<Authorization> state = new AsyncLocal<Authorization>();
    
      public Authorization Current { get => state.Value; set => state.Value = value; }
    }
    

    Code, where authorization is valid, will be:

    Authorization authorization = ... ;
    
    try
    {
      AuthorizationProvider.Current = authorization;
      
      // your logic.
    }
    finally
    {
      AuthorizationProvider.Current = null;
    }
    

    Then you can read the authorization object from OnEntry:

    [Serializable]
    public class LoggingAspect : OnMethodBoundaryAspect
    {
      public override void OnEntry(MethodExecutionArgs args)
      {
        if (!(AuthorizationProvider.Current?.IsValid ?? false))
        {
          ...
        }
      }
    }
    

    You can also wrap the setting/unsetting of the current Authorization in an IDisposable so that you can use using statement.

    Values set assigned in the AsyncLocal are stored in the execution context and as such flow through async calls. The value stored in the execution context which may be captured and used after you remove the value in the finally block above. See more here.