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?
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.
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 .
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.
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.
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();
}
}
}
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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With