Search code examples
c#entity-frameworkunity-containeraop

How to create an aspect decorator to handle EF transactions


I'm working (maintaining) on a dll assembly that acts as a Data Access Layer, there are many methods that requires transaction handling, many other do not, it's a currently "functional" dll, without any transaction handling method, I need to add it, so I'm looking for an easy way to add a transaction handler.

I'm wondering if is it possible to use AOP to create a decorator that I can add to the methods that requires a transaction.

I would like to have something like this:

[Transaction]
void MyDbMethod()
{
  //DoSomething
  myContext.SaveChanges();  
}

For the EF model definition I'm using Code First, the current project uses Unity framework for some other DI tasks, can that framework be used for this?


Solution

  • If someone faces this same issue, I did not found any "by hand" solution, instead I used the PostSharp library and its OnMethodBoundaryAspect class, but be careful, at this moment the free/express license has limitations about the amount of classes where you can use it, so read carefully its limitations.

    using System.Transactions;
    using PostSharp.Aspects;
    using PostSharp.Serialization;
    
    namespace MyProject
    {
        [PSerializable]
        public class Transaction : OnMethodBoundaryAspect
        {
            public Transaction()
            {
                //Required if the decorated method is async
                ApplyToStateMachine = true;
            }
    
            public override void OnEntry(MethodExecutionArgs args)
            {
                //TransactionScopeAsyncFlowOption.Enabled => Required if the decorated method is async
                var transactionScope = new TransactionScope(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled);
                args.MethodExecutionTag = transactionScope;
            }
    
            public override void OnSuccess(MethodExecutionArgs args)
            {
                var transactionScope = (TransactionScope)args.MethodExecutionTag;
                transactionScope.Complete();
            }
    
            public override void OnExit(MethodExecutionArgs args)
            {
                var transactionScope = (TransactionScope)args.MethodExecutionTag;
                transactionScope.Dispose();
            }
        }
    }