You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 03:37:54