Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transactional annotation attribute in .NET Core

I am just curious, in Java, there is a @Transactional attribute which can be placed above the method name and because almost every application service method use's transaction, it may simplify the code.

// Java example
public class FooApplicationService {
    @Transactional
    public void DoSomething() 
    {
        // do something ...
    }
}

This is currently how it is done in .NET

// .NET example
public class FooApplicationService {
    public void DoSomething() 
    {
        using (var transaction = new TransactionScope())
        {
            // do something ...
            transaction.Complete();
        }
    }
}

Is it possible to manage transaction's via annotation attribute in .NET Core too?

like image 369
Muflix Avatar asked Aug 10 '19 10:08

Muflix


People also ask

What is the use of @transactional annotation?

The @Transactional annotation is the metadata that specifies the semantics of the transactions on a method. We have two ways to rollback a transaction: declarative and programmatic. In the declarative approach, we annotate the methods with the @Transactional annotation.

What are attributes in @transactional?

A transaction attribute controls the scope of a transaction. Figure 27–1 illustrates why controlling the scope is important. In the diagram, method-A begins a transaction and then invokes method-B of Bean-2 .

What is transaction attribute in C#?

TransactionAttribute() Initializes a new instance of the TransactionAttribute class, setting the component's requested transaction type to Required. TransactionAttribute(TransactionOption) Initializes a new instance of the TransactionAttribute class, specifying the transaction type.

How do you enable @transactional annotation?

Annotation Type EnableTransactionManagement. Enables Spring's annotation-driven transaction management capability, similar to the support found in Spring's <tx:*> XML namespace. To be used on @Configuration classes to configure traditional, imperative transaction management or reactive transaction management.


2 Answers

You can create action filter for this purpose

//filter factory is used in order to create new filter instance per request
public class TransactionalAttribute : Attribute, IFilterFactory
{
    //make sure filter marked as not reusable
    public bool IsReusable => false;

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        return new TransactionalFilter();
    }

    private class TransactionalFilter : IActionFilter
    {
        private TransactionScope _transactionScope;

        public void OnActionExecuting(ActionExecutingContext context)
        {
            _transactionScope = new TransactionScope();
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            //if no exception were thrown
            if (context.Exception == null)
                _transactionScope.Complete();
        }
    }
}

And use it like this

public class HomeController : Controller {
    //...    

    [Transactional]
    public IActionResult Test() { /*some code */ }

    //...
}

Note

As mentioned in comments by @cmart there is more elegant solution to accomplish this with using IAsyncActionFilter. It's also important to check if no exceptions were thrown as per @notracs comment.

public class TransactionalAttribute : Attribute, IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        using (var transactionScope = new TransactionScope())
        {
            ActionExecutedContext actionExecutedContext = await next();
            //if no exception were thrown
            if (actionExecutedContext.Exception == null)
                transactionScope.Complete();
        }
    }
}
like image 178
Alexander Avatar answered Oct 19 '22 18:10

Alexander


can do this?

using Autofac.Extras.DynamicProxy

  public void Intercept(IInvocation invocation)
        {
            if (!Attribute.IsDefined(invocation.MethodInvocationTarget, typeof(Transaction)))
                invocation.Proceed();
            else
            {
                using (var transaction = new TransactionScope())
                {

                    invocation.Proceed();
                    Type type = invocation.Method.ReturnType;
                    
                    if (type != null && type == typeof(Task))
                    {
                        Func<Task> continuation = async () =>
                            {
                                await (Task)invocation.ReturnValue;
                            };

                        invocation.ReturnValue = continuation();
                    }
                    transaction.Complete();
                }
            }
        }
    }

    [AttributeUsage(AttributeTargets.Method)]
    public class Transaction : Attribute
    {

    }

in application layer

   [Intercept(typeof(TransactionInterceptor))]
    public class Application
    {
        [Transaction]
        public Task Method()
        {
            //Do something
        }
    }

like image 25
wenming Avatar answered Oct 19 '22 17:10

wenming