如何在DbUpdateConcurrencyException并发处理中集成事务?
多客户端并发更新数据的事务与并发处理疑问
我正在开发一款支持多客户端更新同一数据的应用,流程是先更新Fruit数据并存库,再通过SignalR同步到其他所有客户端。为处理并发问题,我参考微软的并发处理文档写了代码,现在尝试在里面集成事务,但不确定这个实现是否正确、有没有必要,同时也疑惑是不是有了并发处理机制就不用事务了。
为测试并发场景,我在dbContext.SaveChanges();处打了断点,同时手动修改数据库里的数据。
API 端点代码
app.MapPut("UpdateFruits", async (string message, IHubContext<ChatHub> context, DatabaseContext dbContext) => { //message(Param) = { "ID":"2","DisplayName":"Fruit","Leaf":null,"State":"Chopped"} using var transaction = dbContext.Database.BeginTransaction(); try { // 从数据库获取Fruit数据并修改 var fruit = dbContext.Set<Fruit>().Single(p => p.ID == 1); fruit.State = State.Chopped; fruit.DisplayName = "test"; var saved = false; while (!saved) { try { // 尝试保存变更到数据库 dbContext.SaveChanges(); saved = true; } catch (DbUpdateConcurrencyException ex) { foreach (var entry in ex.Entries) { if (entry.Entity is Fruit) { var proposedValues = entry.CurrentValues; var databaseValues = entry.GetDatabaseValues(); foreach (var property in proposedValues.Properties) { var proposedValue = proposedValues[property]; var databaseValue = databaseValues[property]; // TODO: 决定要保存到数据库的值 // proposedValues[property] = <要保存的值>; } // 刷新原始值以跳过下一次并发检查 entry.OriginalValues.SetValues(databaseValues); } else { throw new NotSupportedException( "不知道如何处理 " + entry.Metadata.Name + " 的并发冲突"); } } } } transaction.Commit(); var jsonString = JsonConvert.SerializeObject(fruit); await context.Clients.All.SendAsync("ReceiveMessage", "FromServer", jsonString); } catch (Exception) { transaction.Rollback(); } return Results.NoContent(); });
实体模型代码
public class Fruit { public int ID { get; set; } public string DisplayName { get; set; } = String.Empty; public List<Leaf>? Leaf{ get; set; } } public class Leaf { public int ID { get; set; } public string DisplayName { get; set; } = String.Empty; public List<Vein>? Vein{ get; set; } } public class Vein { public int ID { get; set; } public string DisplayName { get; set; } = String.Empty; }
DbContext 代码
using EAS.API.PropertyManagement.Entities; using GFX.Suite.API.GFX.Entities; using Microsoft.EntityFrameworkCore; using System.Collections; namespace EAS.API.PropertyManagement.Core; public class DatabaseContext : DbContext { public DatabaseContext(DbContextOptions options) : base(options) { } Random rnd = new Random(); protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Fruit>(builder => { builder.ToTable("Fruit"); //builder.HasMany(c => c.Leaf) //.WithOne() //.HasForeignKey(f => f.LeafID); //var rendertypes = Enumerable // .Range(1, 5) // .Select(x => new Fruit // { // ID = x, // DisplayName = $"Fruits {x}", // }) // .ToList(); //builder.HasData(rendertypes); }); modelBuilder.Entity<Leaf>(builder => { builder.ToTable("Leaf"); //var properties = Enumerable // .Range(1, 5) // .Select(x => new Leaf // { // ID = x, // DisplayName = $"Leaf {x}", // }) // .ToList(); //builder.HasData(properties); }); modelBuilder.Entity<Vein>(builder => { builder.ToTable("Vein"); //var properties = Enumerable // .Range(1, 5) // .Select(x => new Vein // { // ID = x, // DisplayName = $"Vein {x}", // }) // .ToList(); //builder.HasData(properties); }); } }
内容的提问来源于stack exchange,提问作者Dev
相关产品推荐
相关产品推荐

