前言
在数据库操作过程中,有一个概念是绕不开的,那就是事务。
事务能够确保一系列数据库操作要么全部成功提交,要么全部失败回滚,保证数据的一致性和完整性。
在 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" } }
4.创建一个自定义的 Attribute,用于给无需启用事务控制的操作方法
[AttributeUsage(AttributeTargets.Method)] public class NotTransactionalAttribute:Attribute { }
5.编写自定义的操作筛选器 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(); } } }
6.打开 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();
7.开控制器,增加一个用于测试的操作方法(留意注释)
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(); } } }