在实体框架中使用事务或锁定以确保正确操作

本文关键字:锁定 确保 操作 事务 框架 实体 | 更新日期: 2023-09-27 18:25:49

我对EF和SQL一般来说还很陌生,所以我需要一些帮助来澄清这一点。

假设我有一个表"钱包"(和EF代码第一对象钱包),它有一个ID和一个余额。我需要做这样的操作:

if(wallet.balance > 100){
    doOtherChecksThatTake10Seconds();
    wallet.balance -= 50;
    context.SaveChanges();
}

正如你所看到的,它会检查一个条件是否有效,如果是,它必须先做一系列其他需要很长时间的操作(在这个夸张的例子中,我们说是10秒),然后如果通过,它会从钱包中减去50美元,并保存新数据。

问题是,还有其他事情可以随时改变钱包余额(这是一个网络应用程序)。如果发生这种情况:

  1. walletbalance=110
  2. 此操作通过了"if"检查,因为wallet.ballance>110
  3. 当它执行"doOtherChecksThatTake10Seconds()"时,用户从钱包中转出40美元
  4. 现在walletbalance=70
  5. "doOtherChecksThatTake10Seconds()"完成,从wallet.ballance中减去50,然后用新数据保存上下文

在这种情况下,walletbalance>100的检查不再为真,但由于延迟,操作仍然发生。我需要找到一种方法来锁定表,直到整个操作完成才释放它,这样在操作过程中就不会编辑任何内容。最有效的方法是什么?

需要注意的是,我曾尝试将此操作放入TransactionScope()中,我不确定这是否会产生预期效果,但我确实注意到,它开始导致与正在运行的完全不同的数据库操作发生大量死锁。

在实体框架中使用事务或锁定以确保正确操作

使用优化并发http://msdn.microsoft.com/en-us/data/jj592904

//Object Property:
public byte[] RowVersion { get; set; }
//Object Configuration:
Property(p => p.RowVersion).IsRowVersion().IsConcurrencyToken();

这允许脏读取。但是,当你去更新记录时,系统会检查rowversion在同一时间内没有改变,如果有人在这段时间内改变了记录,它就会失败。每次记录更改时,DB都会维护行版本。

开箱即用EF乐观锁定

您可以使用Transaction Scope。

导入命名空间

using System.Transactions;

并使用如下:

public string InsertBrand()
        {
            try
            {
                using (TransactionScope transaction = new TransactionScope())
                {
                   //Do your operations here
                    transaction.Complete();
                    return "Mobile Brand Added";
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

另一种方法可以是使用一个或多个内部队列,并仅由一个线程使用该队列(生产者-消费者模式)。我在预订系统中使用这种方法,效果很好,非常简单。

在我的案例中,我有多个动态创建和删除的队列(每个"产品"一个)和多个使用者,其中只有一个使用者可以分配给一个队列。这也允许处理更高的并发性。在拥有大量用户的高并发场景中,您还可以使用单独的服务器和队列(如msmq)来处理此问题。

在一个票务系统中,当一部新的《哈利波特》上映时,很多用户都想拥有一张音乐会的门票,或者在购物系统中,这种方法可能会有问题,但我没有这种情况。