翼度科技»论坛 编程开发 .net 查看内容

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

5

主题

5

帖子

15

积分

新手上路

Rank: 1

积分
15

前言

在数据库操作过程中,有一个概念是绕不开的,那就是事务。
事务能够确保一系列数据库操作要么全部成功提交,要么全部失败回滚,保证数据的一致性和完整性。
在 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年+ 资深研发老鸟,是软件项目管理师,也是快乐的程序猿,持续免费分享全栈实用编程技巧、项目管理经验和职场成长心得!欢迎关注老杨的公众号(名称:代码掌控者),和你共同探索代码世界的奥秘!


来源:https://www.cnblogs.com/JackyGz/p/18458974
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

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

x

举报 回复 使用道具