.Net Core Action过滤器使用实例(自动启用事务)

基于前面对Action Filter的介绍,本节通过实例来说明其用法。

.Net Core自定义Action过滤器

数据库事务有一个非常重要的特性,那就是“原子性”,它保证了我们对数据库的多个操作要么全部成功、要么全部失败,进而帮助我们保证业务数据的正确性。

下面代码将实现一个对于数据库操作的自动启用事务的操作筛选器。

使用TransactionScope简化事务代码的编写,TransactionScope是.Net中用来标记一段支持事务的代码的类。EF Core对TransactionScope提供了天然的支持,当一段使用EF Core进行数据库操作的代码放到TransactionScope声明的范围中的时候,这段代码就会自动被标记为“支持事务”。

大多数情况下,操作方法都需要有事务,当一个操作方法不需要自动启用事务控制时,可以给这些操作方法添加一个自定义的NotTransactionalAttribute特性。

NotTransactionalAttribute.cs代码如下:

    /// <summary>
    /// 自定义特性,在不需要自动启用事务控制的方法上添加该特性
    /// </summary>
    [AttributeUsage(AttributeTargets.Method)]
    public class NotTransactionalAttribute : Attribute
    {
    }

编写过滤器TransactionScopeFilter,代码如下:

using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Reflection;
using System.Transactions;

namespace CoreWebApi3
{
    /// <summary>
    /// 自动启用事务的操作筛选器
    /// </summary>
    public class TransactionScopeFilter : IAsyncActionFilter
    {
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            //判断操作方法上是否标记了NotTransactionalAttribute
            bool hasNotTransactionalAttribute = false;
            if (context.ActionDescriptor is ControllerActionDescriptor)
            {
                var actionDesc = (ControllerActionDescriptor)context.ActionDescriptor;
                hasNotTransactionalAttribute = actionDesc.MethodInfo.IsDefined(typeof(NotTransactionalAttribute));
            }

            //如果标记了,则直接执行next
            if (hasNotTransactionalAttribute)
            {
                await next();
                return;
            }

            using (var txScope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
            {
                var result = await next();
                if (result.Exception == null)//操作方法执行没有异常
                {
                    txScope.Complete();//提交事务
                }
            }

           

        }
    }
}

在Program.cs中注册TransactionScopeFilter:

//设置全局的筛选器
builder.Services.Configure<MvcOptions>(option =>
{
    option.Filters.Add<TransactionScopeFilter>();
});

注册代码要写在builder.Build()之前。

最后,在EF CORE插入数据的代码中插入记录:

        [HttpPost]
        public async Task Save() {
            dbCtx.Book.Add(new Book { Id = Guid.NewGuid(), Name = "1", Price = 1 });
            await dbCtx.SaveChangeAsync();

            dbCtx.Book.Add(new Book { Id = Guid.NewGuid(), Name = "2", Price = 2 });
            await dbCtx.SaveChangeAsync();
        }

上面的代码能够正确的插入两条数据。如果在第一个SaveChangeAsync下面加入一行throw new Exception() 来抛出异常,再次执行SaveChangeAsync方法之后,就会发现数据库中没有插入记录。这说明第一个SaveChangeAsync执行后,虽然实现了插入数据,但是由于事务回滚,因此被插入的数据也被回滚了。

猜你喜欢

转载自blog.csdn.net/liangmengbk/article/details/130475175
今日推荐