C#【EF Core框架】使用乐观锁处理并发冲突

3 篇文章 0 订阅

读本文前先掌握乐观锁和悲观锁的概念

使用乐观锁处理并发冲突 

数据库并发指的是多个进程或用户同时访问或更改数据库中的相同数据的情况。 并发控制指的是用于在发生并发更改时确保数据一致性的特定机制。

EF Core 实现乐观并发控制,这意味着它将允许多个进程或用户独立进行更改而不产生同步或锁定的开销。 在理想情况下,这些更改将不会相互干扰,因此都能够成功。 在最坏的情况下,两个或更多进程将尝试进行冲突更改,其中只有一个进程应该成功。

并发控制在 EF Core 中的工作原理

配置为并发令牌的属性用于实现乐观并发控制:每当在 SaveChanges 期间执行更新或删除操作时,会将数据库上的并发令牌值与通过 EF Core 读取的原始值进行比较。

  • 如果这些值匹配,则可以完成该操作。
  • 如果这些值不匹配,EF Core 会假设另一个用户已执行冲突操作,并中止当前事务。

另一个用户已执行与当前操作冲突的操作的情况称为并发冲突

数据库提供程序负责实现并发令牌值的比较。

在关系数据库上,EF Core 包括对任何 UPDATE 或 DELETE 语句的 WHERE 子句中的并发令牌值的检查。 执行这些语句后,EF Core 会读取受影响的行数。

如果未影响任何行,将检测到并发冲突,并且 EF Core 会引发 DbUpdateConcurrencyException

例如,我们可能希望将 Person 上的 LastName 配置为并发令牌。 则针对用户的任何更新操作将包括 WHERE 子句中的并发检查:

SQL复制

UPDATE [Person] SET [FirstName] = @p1
WHERE [PersonId] = @p0 AND [LastName] = @p2;

解决并发冲突

继续前面的示例,如果一个用户尝试保存对 Person 所做的某些更改,但另一个用户已更改 LastName,则将引发异常。

此时,应用程序可能只需通知用户由于发生冲突更改而导致更新未成功,然后继续操作。 但可能需要提示用户确保此记录仍表示同一实际用户并重试该操作。

此过程是解决并发冲突的一个示例。

解决并发冲突涉及将当前 DbContext 中挂起的更改与数据库中的值进行合并。 要合并的值将根据应用程序的不同而有所不同,并且可能由用户输入指示。

有三组值可用于帮助解决并发冲突:

  • “当前值”是应用程序尝试写入数据库的值。
  • “原始值”是在进行任何编辑之前最初从数据库中检索的值。
  • “数据库值”是当前存储在数据库中的值。

处理并发冲突的常规方法是:

  1. 在 SaveChanges 期间捕获 DbUpdateConcurrencyException
  2. 使用 DbUpdateConcurrencyException.Entries 为受影响的实体准备一组新更改。
  3. 刷新并发令牌的原始值以反映数据库中的当前值。
  4. 重试该过程,直到不发生任何冲突。

在下面的示例中,将 Person.FirstName 和 Person.LastName 设置为并发令牌。 在包括应用程序特定逻辑以选择要保存的值的位置处有一条 // TODO: 注释。

注意需要在实体类需要进行乐观锁的属性标记为[ConcurrencyCheck]

 [Table("Infrastructure_User")]
    public class Infrastructure_User
    {
        [Key]
        [Required]
        [Column("userId")]
        public int UserId { get; set; }

        //需要进行乐观锁比较的标记
        [ConcurrencyCheck]
        [Column("displayName")]
        public string DisplayName { get; set; }

        [Column("login")]
        public string Login { get; set; }
    }
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
namespace EFLearn
{
    class Program
    {
        static async System.Threading.Tasks.Task Main(string[] args)
        {
            ApplicationDbContext  context = new ApplicationDbContext();

            var items = context.Source.First();

            items.DisplayName =  "管理员2";

            context.Database.ExecuteSqlRaw("update infrastructure_User set DisplayName='管理员1' where userid=1");

            var saved = false;

            while (!saved)
            {
                try
                {
                    // Attempt to save changes to the database
                    context.SaveChanges();
                    saved = true;
                }
                catch (DbUpdateConcurrencyException ex)
                {
                    foreach (var entry in ex.Entries)
                    {
                        if (entry.Entity is Infrastructure_User)
                        {
                            var proposedValues = entry.CurrentValues;
                            var databaseValues = entry.GetDatabaseValues();

                            foreach (var property in proposedValues.Properties)
                            {
                                var proposedValue = proposedValues[property];
                                var databaseValue = databaseValues[property];
                            }

                            entry.OriginalValues.SetValues(databaseValues);
                        }
                        else
                        {
                            throw new NotSupportedException(
                                "Don't know how to handle concurrency conflicts for "
                                + entry.Metadata.Name);
                        }
                    }
                }
            }


            Console.WriteLine("执行结束");
        }
    }
}

下一篇 C#【EF Core框架】进行数据库事务处理https://blog.csdn.net/lichao19897314/article/details/122881390

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小耕家的喵大仙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值