ActionFilter

ActionFilter

# 多个ActionFilter存在时:
1、只有一个ActionFilter执行了await next(),那么一定会执行await next()下面的代码
2、若异常发生第一个ActionFilter await next()的前后代码中,那程序程序会被终止,且异常不会被其他ActionFilter捕获
3、若异常发生其他ActionFilter await next()的前后代码中,异常都可以被其他ActionFilter捕获,且发生异常的那个地方以下的代码将不会执行
4、Action中发生异常所有的ActionFilter后代码还会执行并都将捕获到异常信息

1、方法过滤器实现事务回滚

2、异步方法中的事务

# 在一个存在await的异步方法中,TransactionScope需要在构造函数中传入TransactionScopeAsyncFlowOption,否则报错
A TransactionScope must be disposed on the same thread that it was created.

# 因为在同步状态下TransactionScope内部其实是去找了ThreadLocal(相当于当前线程的全局上下文),所以可以知晓所有数据变化,作出回滚

# 而在异步下,就得去找异步版的ThreadLocal--AsyncLocal,所以TransactionScopeAsyncFlowOption相当于是告诉TransactionScope去激活AsyncLocal

3、ActionFilter结合特性并实现事务回滚

NotTransationAttribute.cs

using System;

namespace Application
{
    /// <summary>
    /// 定义特性
    /// </summary>
    [AttributeUsage(AttributeTargets.All)]
    public class NotTransationAttribute : Attribute
    {
    }
}

ActionFilter.cs

using Application;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Linq;
using System.Threading.Tasks;
using System.Transactions;

namespace Infrastructure
{
    public class TransationScopeFilter : IAsyncActionFilter
    {
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            // 当前被执行的Action里面的描述信息:context.ActionDescriptor
            // 当前被执行的Actionmi里外面都有的描述信息: ControllerActionDescriptor
            // 为什么需要ControllerActionDescriptor呢?因为asp net core 不仅仅只有webapi/mvc,还有blazer、razor、pages等等,所以context.ActionDescriptor的类型是符合多元化的,但是具体是哪个需要自己指明,这里就指定了使用webapi/mvc的ControllerActionDescriptor
            // 所以 ControllerActionDescriptor是继承自context.ActionDescriptor。
            ControllerActionDescriptor ctrlActionDesc = context.ActionDescriptor as ControllerActionDescriptor;

            bool isTx = false;
            //如果方法或控制器上标记了NotTransationAttribute则为true
            bool hasNotTransationAttribute  = ctrlActionDesc.ControllerTypeInfo.GetCustomAttributes(typeof(NotTransationAttribute),false).Any()
             || ctrlActionDesc.MethodInfo.GetCustomAttributes(typeof(NotTransationAttribute), false).Any();

             isTx = !hasNotTransationAttribute;


            //标记则走事务
            if (isTx)
            {
                using (TransactionScope tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                {
                    var result = await next();
                    if (result.Exception == null)
                    {
                        tx.Complete();
                    }
                }
            }
            else//否则直接放行
            {
                await next();
            }


            
        }
    }
}

Startup.cs

        public void ConfigureServices(IServiceCollection services)
        {
            //添加服务
            services.Configure<MvcOptions>(opt=> {
                //如果有多个ActionFilter,此ActionFilter应排在第一个,目的是把其他ActionFilter中也有数据库操作的一并回滚
                opt.Filters.Add<TransationScopeFilter>();
            });
            services.AddDbContext<MyDbContext>(opt => {
                opt.UseSqlServer("Server=.;Database=tx;Trusted_Connection=True;");
            });
        }

WeatherForecastController.cs

using Application;
using Domain;
using Infrastructure;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using System.Transactions;

namespace ActionFilter.Controllers
{
    [ApiController]
    [Route("[controller]")]
    //[NotTransationAttribute]
    public class WeatherForecastController : ControllerBase
    {
        private readonly MyDbContext db;

        public WeatherForecastController(MyDbContext db)
        {
            this.db = db;
        }
        //============================通过ActionFilter和Attribute使用TransactionScope====================================

        /// <summary>
        /// 测试TransationScopeFilter
        /// </summary>
        /// <returns></returns>
        [HttpPost("ActionFilterAttribute1")]
        public async Task<string> Test8()
        {

           db.Persons.Add(new Person { Name = "zyf", Age = 18 });
           await db.SaveChangesAsync();
           db.Books.Add(new Book { Name = "梦回金沙湾", Price = 88 });
           await db.SaveChangesAsync();

           return "ok";           
        }
        /// <summary>
        /// 标记NotTransationAttribute不使用TransationScopeFilter
        /// </summary>
        /// <returns></returns>
        [NotTransationAttribute]
        [HttpPost("ActionFilterAttribute2")]
        public async Task<string> Test9()
        {
            db.Persons.Add(new Person { Name = "zyf", Age = 18 });
            await db.SaveChangesAsync();
            db.Books.Add(new Book { Name = "梦回金沙湾", Price = 88 });
            await db.SaveChangesAsync();

            return "ok";
        }


    }
}

# 结论:
# Book.cs和Person.cs我都限制Name属性长度为3。
执行ActionFilterAttribute1方法:由于此方法没有标记[NotTransationAttribute],你将会发现数据库的Person添加成功和Book添加失败
执行ActionFilterAttribute2方法:由于此方法标记[NotTransationAttribute],你将会发现数据库的Person和Book都添加失败



# 项目源码:04-ActionFilter自动启动事务小项目
https://github.com/zpp99love/NetCore.g

4、ActionFilter结合缓存实现请求限流

将ip存入缓存中,并同时设置绝对滑动、随机过期时间,防止缓存雪崩等问题

posted @ 2023-02-05 10:02  long-livece  阅读(73)  评论(0编辑  收藏  举报