操纵筛选器的 1 个应用实例:自动启用事件

农民  金牌会员 | 2024-10-11 18:12:00 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 873|帖子 873|积分 2619


前言

在数据库操纵过程中,有一个概念是绕不开的,那就是事件。
事件能够确保一系列数据库操纵要么全部成功提交,要么全部失败回滚,包管数据的同等性和完整性。
在 Asp.Net Core Web API 中,我们可以使用操纵筛选器给全部的数据库操纵 API 加上事件控制,省心又省力,效果还很好。
看看 Step By Step 步骤是如何实现上述功能的。
Step By Step 步骤


  • 创建一个 ASP.NET Core Web API 项目
  • 引用 EF Core 项目 BooksEFCore

  • 打开 appsettings.json,添加数据库连接串
    1. {
    2.   "Logging": {
    3.         "LogLevel": {
    4.           "Default": "Information",
    5.           "Microsoft.AspNetCore": "Warning"
    6.         }
    7.   },
    8.   "AllowedHosts": "*",
    9.   "ConnectionStrings": {
    10.         "Default": "Server=(localdb)\\mssqllocaldb;Database=TestDB;Trusted_Connection=True;MultipleActiveResultSets=true"
    11.   }
    12. }
    复制代码
  • 创建一个自界说的 Attribute,用于给无需启用事件控制的操纵方法
    1. [AttributeUsage(AttributeTargets.Method)]
    2. public class NotTransactionalAttribute:Attribute
    3. {
    4. }
    复制代码
  • 编写自界说的操纵筛选器 TransactionScopeFilter,用于自动启用事件控制(留意解释
    1. using Microsoft.AspNetCore.Mvc.Controllers;
    2. using Microsoft.AspNetCore.Mvc.Filters;
    3. using System.Reflection;
    4. using System.Transactions;
    5. public class TransactionScopeFilter : IAsyncActionFilter
    6. {
    7.         public async Task OnActionExecutionAsync(
    8.                 ActionExecutingContext context,
    9.                 ActionExecutionDelegate next)
    10.         {
    11.                 bool hasNotTransactionalAttribute = false;
    12.                 if (context.ActionDescriptor is ControllerActionDescriptor)
    13.                 {
    14.                         var actionDesc = (ControllerActionDescriptor)context.ActionDescriptor;
    15.                         //判断操作方法上是否标注了NotTransactionalAttribute
    16.                         hasNotTransactionalAttribute = actionDesc.MethodInfo.IsDefined(typeof(NotTransactionalAttribute));
    17.                 }
    18.                 //如果操作方法标注了NotTransactionalAttribute,直接执行操作方法
    19.                 if (hasNotTransactionalAttribute)
    20.                 {
    21.                         await next();
    22.                         return;
    23.                 }
    24.                 //如果操作方法没有标注NotTransactionalAttribute,启用事务
    25.                 using var txScope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
    26.                 var result = await next();
    27.                 if (result.Exception == null)
    28.                 {
    29.                         txScope.Complete();
    30.                 }
    31.         }
    32. }
    复制代码
  • 打开 Program.cs,注册这个操纵筛选器
    1. using Microsoft.AspNetCore.Mvc;
    2. using Microsoft.EntityFrameworkCore;
    3. var builder = WebApplication.CreateBuilder(args);
    4. // Add services to the container.
    5. builder.Services.AddControllers();
    6. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
    7. builder.Services.AddEndpointsApiExplorer();
    8. builder.Services.AddSwaggerGen();
    9. // 注册数据库服务
    10. builder.Services.AddDbContext<MyDbContext>(opt => {
    11.         string connStr = builder.Configuration.GetConnectionString("Default");
    12.         opt.UseSqlServer(connStr);
    13. });
    14. // 注册自动启用事务过滤器
    15. builder.Services.Configure<MvcOptions>(opt => {
    16.         opt.Filters.Add<TransactionScopeFilter>();
    17. });
    18. var app = builder.Build();
    19. // Configure the HTTP request pipeline.
    20. if (app.Environment.IsDevelopment())
    21. {
    22.         app.UseSwagger();
    23.         app.UseSwaggerUI();
    24. }
    25. app.UseHttpsRedirection();
    26. app.UseAuthorization();
    27. app.MapControllers();
    28. app.Run();
    复制代码
  • 打开控制器,增加一个用于测试的操纵方法(留意解释
    1. using Microsoft.AspNetCore.Mvc;
    2. namespace 自动启用事务的筛选器.Controllers
    3. {
    4.         [ApiController]
    5.         [Route("[controller]/[action]")]
    6.         public class TestController : ControllerBase
    7.         {
    8.                 private readonly MyDbContext dbCtx;
    9.                 public TestController(MyDbContext dbCtx)
    10.                 {
    11.                         this.dbCtx = dbCtx;
    12.                 }
    13.                 [HttpPost]
    14.                 public async Task Save()
    15.                 {
    16.                         dbCtx.Books.Add(new Book { Id = Guid.NewGuid(), Name = "1", Price = 1 });
    17.                         await dbCtx.SaveChangesAsync();
    18.                         dbCtx.Books.Add(new Book { Id = Guid.NewGuid(), Name = "2", Price = 2 });
    19.                         await dbCtx.SaveChangesAsync();
    20.                         // 以上代码能够正确地插入两条数据
    21.                         // 如果启用以下代码抛出异常,将不会插入数据
    22.                         // 说明事务起作用,数据被回滚了
    23.                         // throw new Exception();
    24.                 }
    25.         }
    26. }
    复制代码
总结


  • 可以使用 TransactionScope 简化事件代码的编写。
  • TransactionScope 是 .NET 中用来标记一段支持事件的代码的类。
  • EF Core 对 TransactionScope 提供了天然的支持,当一段使用 EF Core 进行数据库操纵的代码放到 TransactionScope 声明的范围中的时候,这段代码就会自动被标记为 "支持事件"
  • TransactionScope 实现了 IDisposable 接口,如果一个 TransactionScope 的对象没有调用 Complete 就执行了 Dispose 方法,则事件会被回滚,否则事件就会被提交
  • TransactionScope 还支持嵌套式事件,也就是多个 TransactionScope 嵌套,只有最外层的 TransactionScope 提交了事件,全部的操纵才见效;如果最外层的 TransactionScope 回滚了事件,那么即使内层的 TransactionScope 提交了事件,最终全部的操纵仍然会被回滚
  • .NET Core 使用的 TransactionScope 支持的是 "最终同等性"。所谓的 "最终同等性",指的是在一段时间内,如果体系没有发生新的更新操纵,那么全部副本的数据最终会到达同等的状态。换句话说,即使在体系中的不同节点上,数据的更新大概会有一段时间的耽误,但最终全部节点的数据会到达同等的状态。
  • 在同步代码中,TransactionScope 使用 ThreadLocal 关联事件信息;
  • 在异步代码中,TransactionScope 使用 AsyncLocal 关联事件信息
我是老杨,一个执着于编程兴趣、至今奋斗在一线的 10年+ 资深研发老鸟,是软件项目管理师,也是快乐的程序猿,持续免费分享全栈实用编程技巧、项目管理经验和职场成长心得!欢迎关注老杨的公众号(名称:代码掌控者),和你共同探索代码世界的奥秘!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

农民

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表