1

I have an AOP auditing library that intercepts methods using Castle DynamicProxy with Autofac and checks if they have this specific attribute:

[Audit(AuditOperation.Create), MethodImpl(MethodImplOptions.NoInlining)]
public virtual TEntity Add(TEntity entity, [MethodIsAuditableParam]bool methodIsAuditable = true)
{
   entity = entity.DeleteNestedProperties();

   using (var entityContext = ContextCreator())
   {
      TEntity addedEntity = entityContext.Set<TEntity>().Add(entity);
      entityContext.SaveChanges();

      return addedEntity;
   }
}

This is a generic repository method that adds a generic entity to the database. But I also have specific methods in my Domain layer that do additional stuff and then call these generic repository methods:

[Audit(AuditOperation.Create), MethodImpl(MethodImplOptions.NoInlining)]
public virtual User AddUser(int currentUserId, User newUser, [MethodIsAuditableParam]bool methodIsAuditable = true)
{
   try
   {
      UserDBValidations(currentUserId, newUser);

      if (newUser.UserProfileId == (int)UserProfileType.Admin ||
         newUser.UserProfileId == (int)UserProfileType.ProcessSpecialist)
         newUser.UGBs = _ugbRepository.FindAll().ToList();

      _userRepository.Add(newUser);

      return newUser;
   }
   catch (Exception ex)
   {
      throw new Exception("Erro no cadastro de usuário: " + ex.Message);
   }       
}

My intention is that if an auditable Domain method (that can call many generic repository methods) exists, my auditing interceptor will audit only this Domain method and will ignore the generic repository audition. But if I call the generic repository methods directly, they will be audited normally.

I tried to achieve this behavior using the stack trace in my interceptor. This following method checks if the stack frames array already contains an IInterceptor.Intercept method. :

private bool CheckIfMethodCallersAreBeingIntercepted()
{
   StackTrace stackTrace = new StackTrace();
   StackFrame[] stackFrames = stackTrace.GetFrames();
   //the int number should match the exact number of method calls in this interception until this current method
   StackFrame[] realStackFrames = stackFrames.SubArray(5, stackFrames.Count() - 5);

   foreach (StackFrame realStackFrame in realStackFrames)
   {
      MethodBase method = realStackFrame.GetMethod();
      if (method == null)
         continue;

      Type declaringType = method.DeclaringType;
      if (declaringType == null)
         continue;

      string declaringTypeFullName = declaringType.FullName;
      if (declaringTypeFullName == null)
         continue;

      if(declaringTypeFullName
         .Contains("AwesomeAuditing.AwesomeAuditingInterceptor"))
         return true;
   }

   return false;
}

But this doesn't work because I can intercept a method and don't audit it:

public void Intercept(IInvocation invocation)
{
   if (!CheckIfMethodShouldBeIntercepted(invocation.Method, invocation.Arguments))
   {
      invocation.Proceed();
      return;
   }

   if (!CheckIfMethodReturnTypeInheritAuditEntity(invocation.Method))
      throw new AwesomeAuditingException(@"The audited method's return type most inherit the IAuditableEntity interface.");

   invocation.Proceed();

   if (CheckIfMethodIsAsync(invocation.Method))
      invocation.ReturnValue = AsyncProceedExecution((dynamic)invocation.ReturnValue, invocation);
   else
      AfterProceedExecution(invocation.ReturnValue, invocation);
}

I don't want to manage a static collection with all current intercepted methods because I want this AOP library to work on many types of projects (Web API, Windows Services, Console Applications, WPF, etc.) and each type has its own way to manage static instances. For example, in a Web API context I don't want that two HTTP request share this static collection. In a Windows Service I don't want to share this static collection between two execution threads.

Do you guys know a better way to manage this multi level interception? Do you know any good practice that will work good in many project types?

Rodrigo
  • 53
  • 1
  • 6

0 Answers0