代码之家  ›  专栏  ›  技术社区  ›  Dev

如何将事务与DbUpdateConcurrentException集成

  •  0
  • Dev  · 技术社区  · 3 年前

    我正在创建一个应用程序,它有多个客户端,可以更新相同的数据,所以我计划使用这种方法更新水果。将它们保存到数据库中,然后使用SignalR更新所有其他客户端。我正在努力设计代码以避免并发问题,所以我从 here 我想知道如何将事务与这段代码结合起来(我已经尝试过,但不确定它是否正确或必要)?或者因为它是为了处理并发性而设计的,我甚至需要事务吗?

    为了创建并发问题,我将断点设置为 dbContext.SaveChanges(); 并更新数据库中的数据。

    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
        {
            // Fetch a person from database and change phone number
            var fruit = dbContext.Set<Fruit>().Single(p => p.ID == 1);
            fruit.State = State.Chopped;
            fruit.DisplayName = "test";
    
            var saved = false;
            while (!saved)
            {
                try
                {
                    // Attempt to save changes to the database
                    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: decide which value should be written to database
                                // proposedValues[property] = <value to be saved>;
                            }
    
                            // Refresh original values to bypass next concurrency check
                            entry.OriginalValues.SetValues(databaseValues);
                        }
                        else
                        {
                            throw new NotSupportedException(
                                "Don't know how to handle concurrency conflicts for "
                                + 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);
            });
        }
    }
    
    0 回复  |  直到 3 年前