前言
在数据库操作过程中,有一个概念是绕不开的,那就是事务。
事务能够确保一系列数据库操作要么全部成功提交,要么全部失败回滚,保证数据的一致性和完整性。
在 Asp.Net Core Web API 中,我们可以使用操作筛选器给所有的数据库操作 API 加上事务控制,省心又省力,效果还很好。
看看 Step By Step 步骤是如何实现上述功能的。
Step By Step 步骤
-
创建一个 ASP.NET Core Web API 项目
-
引用 EF Core 项目 BooksEFCore
- BooksEFCore 项目创建参见前文《EF Core 在实际开发中,如何分层?》
-
打开
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;//判断操作方法上是否标注了NotTransactionalAttributehasNotTransactionalAttribute = 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
关联事务信息