dimanche 27 août 2017

C#: Logging method calls with AOP Interceptor ... without IOC in place

AOP (Aspect-oriented programming) is used to intercept processes on an application, ex: for intercept method's executions.
Main IOC framework provide a way to do that, but what if we don't have Dependency Injection in place and we want the interception behavior ?

Let's say we want to log calls to a methods in a database repository class, when entering on method, when exiting, when any error occurs and the count of items returned.

Sample code could be:

 public class UserRepository   
 {   
     private static readonly ILog Logger = LogManager.GetLogger(typeof(UserRepository));    
     public virtual List<User> GetUsers()   
     {   
         Logger.Info("getting user list...");   
         try   
         {   
             var result = db.Users.LoadAll(); // here we do the data retrieval   
             Logger.Info($"{result.Count}");   
             return result;   
         }   
         catch(Exception e)   
         {   
             Logger.Error(e);   
             throw;   
         }      
     }   
 }   

So, obviously to include all this lines around DB access in every repository method is very tedious.

As said before, with an IOC framework is very easy to do that, but what if we have no IOC in place and it is too costly to introduce it on existing application like legacy code ?

DinamicProxy from Castle Project is a very simple way to create a Proxy for every class we want to intercept their method calls.

The Proxy will act as a derived class from our target class, and the Interceptor will receive all method's call.

We create a proxy and provide an Interceptor as follow:

var userRepo = new ProxyGenerator().CreateClassProxy<UserRepository>(new DbRepositoryInterceptor());  

DbRepositoryInterceptor implement IInterceptor. This interface contains the method:

void Intercept(IInvocation invocation);

The Interceptor looks as follow:

 public class DbRepositoryInterceptor : IInterceptor  
 {  
      public void Intercept(IInvocation invocation)  
      {  
           PreProceed(invocation);  
           PerformProceed(invocation);  
           PostProceed(invocation);  
      }  
      protected void PreProceed(IInvocation invocation)  
      {  
           Logger.Info($"Execution begin...");  
      }  
      protected void PerformProceed(IInvocation invocation)  
      {  
           invocation.Proceed();  
      }  
      protected void PostProceed(IInvocation invocation)  
      {  
           Logger.Info($"Execution end.");  
      }  
 }  

All calls to methods on class UserRepository will go through Intercept() method on interceptor class, like classic AOP aspect.

If we dont want to intercept all methods but just some of them we can create an Attribute to add to target methods only:

 public class MethodInterceptorAttribute : Attribute  
 {  
 }  

Now, lets add some logic to Interceptor to deal with the attribute:

 public class DbRepositoryInterceptor : IInterceptor  
 {  
      public void Intercept(IInvocation invocation)  
      {  
           var intercept = PerformInterception(invocation);  
           if (!intercept)  
                invocation.Proceed();  
           else  
           {  
                PreProceed(invocation);  
                PerformProceed(invocation);  
                PostProceed(invocation);  
           }  
      }  
      protected void PreProceed(IInvocation invocation)  
      {  
           Logger.Info($"Execution of query started...");  
      }  
      protected void PerformProceed(IInvocation invocation)  
      {  
           try  
           {  
                invocation.Proceed();  
           }  
           catch (Exception ex)  
           {  
                Logger.Info($"Error during executing query on DB: " + ex);  
                throw;  
           }  
      }  
      protected void PostProceed(IInvocation invocation)  
      {  
           Logger.Info($"Execution of query end.");  
           var result = invocation.ReturnValue;  
           if (result is IList)  
           {  
                Logger.Info($"returns: {((IList)result).Count} items");  
           }  
      }  
      private bool PerformInterception(IInvocation invocation)  
      {  
           var aopAttr = invocation.Method.GetCustomAttributes(typeof(MethodInterceptorAttribute), true);  
           return aopAttr != null;  
      }  
 }  

Above we have the basic Interceptor, with logs before and after method execution, exception handling around the target method and simple logic to log count of items returned. All without need to disrupt existing code with IOC framework introduction.


Aucun commentaire:

Enregistrer un commentaire