前言
在数据库操纵过程中,有一个概念是绕不开的,那就是事件。
事件能够确保一系列数据库操纵要么全部成功提交,要么全部失败回滚,包管数据的同等性和完整性。
在 Asp.Net Core Web API 中,我们可以使用操纵筛选器给全部的数据库操纵 API 加上事件控制,省心又省力,效果还很好。
看看 Step By Step 步骤是如何实现上述功能的。
Step By Step 步骤
- 创建一个 ASP.NET Core Web API 项目
- 引用 EF Core 项目 BooksEFCore
- 打开 appsettings.json,添加数据库连接串
- {
- "Logging": {
- "LogLevel": {
- "Default": "Information",
- "Microsoft.AspNetCore": "Warning"
- }
- },
- "AllowedHosts": "*",
- "ConnectionStrings": {
- "Default": "Server=(localdb)\\mssqllocaldb;Database=TestDB;Trusted_Connection=True;MultipleActiveResultSets=true"
- }
- }
复制代码 - 创建一个自界说的 Attribute,用于给无需启用事件控制的操纵方法
- [AttributeUsage(AttributeTargets.Method)]
- public class NotTransactionalAttribute:Attribute
- {
- }
复制代码 - 编写自界说的操纵筛选器 TransactionScopeFilter,用于自动启用事件控制(留意解释)
- using Microsoft.AspNetCore.Mvc.Controllers;
- using Microsoft.AspNetCore.Mvc.Filters;
- using System.Reflection;
- using System.Transactions;
- public class TransactionScopeFilter : IAsyncActionFilter
- {
- public async Task OnActionExecutionAsync(
- ActionExecutingContext context,
- ActionExecutionDelegate next)
- {
- bool hasNotTransactionalAttribute = false;
- if (context.ActionDescriptor is ControllerActionDescriptor)
- {
- var actionDesc = (ControllerActionDescriptor)context.ActionDescriptor;
- //判断操作方法上是否标注了NotTransactionalAttribute
- hasNotTransactionalAttribute = actionDesc.MethodInfo.IsDefined(typeof(NotTransactionalAttribute));
- }
- //如果操作方法标注了NotTransactionalAttribute,直接执行操作方法
- if (hasNotTransactionalAttribute)
- {
- await next();
- return;
- }
- //如果操作方法没有标注NotTransactionalAttribute,启用事务
- using var txScope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
- var result = await next();
- if (result.Exception == null)
- {
- txScope.Complete();
- }
- }
- }
复制代码 - 打开 Program.cs,注册这个操纵筛选器
- using Microsoft.AspNetCore.Mvc;
- using Microsoft.EntityFrameworkCore;
- var builder = WebApplication.CreateBuilder(args);
- // Add services to the container.
- builder.Services.AddControllers();
- // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
- builder.Services.AddEndpointsApiExplorer();
- builder.Services.AddSwaggerGen();
- // 注册数据库服务
- builder.Services.AddDbContext<MyDbContext>(opt => {
- string connStr = builder.Configuration.GetConnectionString("Default");
- opt.UseSqlServer(connStr);
- });
- // 注册自动启用事务过滤器
- builder.Services.Configure<MvcOptions>(opt => {
- opt.Filters.Add<TransactionScopeFilter>();
- });
- var app = builder.Build();
- // Configure the HTTP request pipeline.
- if (app.Environment.IsDevelopment())
- {
- app.UseSwagger();
- app.UseSwaggerUI();
- }
- app.UseHttpsRedirection();
- app.UseAuthorization();
- app.MapControllers();
- app.Run();
复制代码 - 打开控制器,增加一个用于测试的操纵方法(留意解释)
- using Microsoft.AspNetCore.Mvc;
- namespace 自动启用事务的筛选器.Controllers
- {
- [ApiController]
- [Route("[controller]/[action]")]
- public class TestController : ControllerBase
- {
- private readonly MyDbContext dbCtx;
- public TestController(MyDbContext dbCtx)
- {
- this.dbCtx = dbCtx;
- }
- [HttpPost]
- public async Task Save()
- {
- dbCtx.Books.Add(new Book { Id = Guid.NewGuid(), Name = "1", Price = 1 });
- await dbCtx.SaveChangesAsync();
- dbCtx.Books.Add(new Book { Id = Guid.NewGuid(), Name = "2", Price = 2 });
- await dbCtx.SaveChangesAsync();
- // 以上代码能够正确地插入两条数据
- // 如果启用以下代码抛出异常,将不会插入数据
- // 说明事务起作用,数据被回滚了
- // throw new Exception();
- }
- }
- }
复制代码 总结
- 可以使用 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企服之家,中国第一个企服评测及商务社交产业平台。 |