EF Core悲观并发控制


前言

Entity Framework (EF) Core 默认主要支持 乐观并发控制(通过并发令牌或 RowVersion),而 悲观并发控制 需要开发者手动实现(通常借助数据库事务和锁机制

一、悲观并发控制的核心思想

操作数据前 显式锁定资源(如行锁表锁),阻止其他事务修改,直到当前事务完成。通常通过数据库的锁机制实现。

二、实现步骤

  1. 开启事务
    使用 BeginTransactionBeginTransactionAsync 开启事务。
  2. 显式加锁查询
    使用 FromSqlRawFromSqlInterpolated 编写原生 SQL 查询,指定锁机制(如 UPDLOCK)。
  3. 操作数据
    在事务内修改数据,此时锁保持有效。
  4. 提交事务
    提交后释放锁。(trans.Commit();)

1)针对SQL Server数据库

  1. 示例

    using (var transaction = context.Database.BeginTransaction())
    {
        try
        {
            // 查询并锁定目标行(SQL Server 使用 UPDLOCK)
            long houseId = 1;
            var house = context.Houses
                .FromSqlInterpolated($"SELECT * FROM T_Houses WITH (UPDLOCK) WHERE Id = {houseId}")
                .FirstOrDefault();
    
            if (string.IsNullOrEmpty(house.Owner))
            {
                // 修改数据
                house.Owner = paramName;
                context.SaveChanges();
            }
    
            // 提交事务,释放锁
            transaction.Commit();
        }
        catch (Exception)
        {
            transaction.Rollback();
            throw;
        }
    }
    

2)针对MySQL数据库

  1. 示例:

    Console.WriteLine("请输入名字");
    string name=Console.ReadLine();
    using (MyDBContext dbContext=new MyDBContext())
    using (var trans=dbContext.Database.BeginTransaction())
    {
        Console.WriteLine($"{DateTime.Now}-准备select for Update");
        var house=dbContext.Houses.FromSqlInterpolated($"select * from T_Houses where Id=1 for update").Single();
        Console.WriteLine($"{DateTime.Now}-完成select for Update");
        if (!string.IsNullOrEmpty(house.Owner))
        {
            if (house.Owner==name)
            {
                Console.WriteLine("此房源已被您抢到");
            }
            else
            {
                Console.WriteLine($"此房源已被{house.Owner}抢占");
            }
            trans.Commit();
            Console.ReadLine();        
            return;
        }
        house.Owner=name;
        Thread.Sleep(5000);
        Console.WriteLine($"恭喜{name}抢到了房源{house.Name}");
        dbContext.SaveChanges();
        trans.Commit();
        Console.ReadLine();
    }
    

三、不同数据库的锁语法

  1. SQL Server: WITH (UPDLOCK) 或 WITH (ROWLOCK)
  2. PostgreSQL: SELECT … FOR UPDATE
  3. MySQL: SELECT … FOR UPDATE

四、注意事项

  1. 事务范围
    保持事务尽可能短,避免长时间锁定导致性能问题。
  2. 死锁风险
    确保锁定顺序一致,减少死锁概率。
  3. 数据库兼容性
    不同数据库的锁语法和行为可能不同,需参考具体数据库文档。

五、适用场景

  1. 高竞争环境(如库存扣减、抢购)
  2. 需要强制串行化操作的场景

六、与乐观并发的对比

悲观并发乐观并发
实现方式显式加锁(事务+锁机制)版本检查(并发令牌、RowVersion)
性能高竞争时可能更高效低冲突时更高效
复杂度需要手动管理锁和事务EF Core 内置支持

总结

EF Core 实现悲观并发需通过数据库锁和事务手动控制。虽然灵活,但需谨慎处理锁的范围和事务生命周期,避免性能问题。如果业务允许,优先考虑乐观并发(如 ConcurrencyCheck 特性或 RowVersion)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值