Jorge Ramírez Jorge Ramírez - 2 months ago 27
C# Question

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?

Answer

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