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

C# 在实体框架中使用事务或锁定以确保正确操作,c#,sql,sql-server,asp.net-mvc,entity-framework,C#,Sql,Sql Server,Asp.net Mvc,Entity Framework,一般来说,我对EF和SQL还比较陌生,所以我需要一些帮助来澄清这一点 假设我有一个表“钱包”(和EF代码第一个对象钱包),它有一个ID和一个余额。我需要做这样的手术: if(wallet.balance > 100){ doOtherChecksThatTake10Seconds(); wallet.balance -= 50; context.SaveChanges(); } 如你所见,它检查一个条件是否有效,如果是,它必须先做一系列其他操作,这需要很长时间(在

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

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

if(wallet.balance > 100){
    doOtherChecksThatTake10Seconds();
    wallet.balance -= 50;
    context.SaveChanges();
}
如你所见,它检查一个条件是否有效,如果是,它必须先做一系列其他操作,这需要很长时间(在这个夸张的例子中,我们说10秒),然后如果通过,它从钱包中减去50美元并保存新数据

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

  • 钱包余额=110
  • 此操作通过其“如果”检查,因为wallet.balance>110
  • 当它执行“DootherChecksthattakeo10seconds()”时,用户会从钱包中转账40美元
  • 现在钱包余额=70
  • “dootherchecksthatake10seconds()”完成,从wallet.balance中减去50,然后用新数据保存上下文
  • 在这种情况下,wallet.balance>100的检查不再正确,但由于延迟,操作仍然发生。我需要找到一种方法来锁定表,在整个操作完成之前不释放它,这样在整个过程中不会编辑任何内容。最有效的方法是什么


    应该注意的是,我已经尝试将此操作放在TransactionScope()中,我不确定这是否会产生预期的效果,但我确实注意到它在运行的一个完全不同的数据库操作中开始导致大量死锁。

    使用乐观并发

    这允许脏读。但是,当您去更新记录时,系统会检查行版本是否同时发生更改,如果有人同时更改了记录,则会失败。 每次记录更改时,数据库都会维护Rowversion


    开箱即用的EF乐观锁定。

    您可以使用事务范围

    导入名称空间

    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)来处理此问题


    当一部新的《哈利·波特》上映时,这种方法可能会出现问题,因为很多用户都想买一张音乐会的票,或者在购物系统中,但我没有这种场景。

    这种模式有效,但最好在不同的场景中使用。尤其是与MSDTC结合使用时。例如跨服务器/跨数据库挑战。所描述的问题是经典的乐观锁定场景。与EF结合使用时,可能会出现性能和隔离级别问题。只要谷歌一下,你就会看到一些论坛帖子。我个人不建议有人在EF中不必要地使用这种方法。
    public string InsertBrand()
            {
                try
                {
    
                    using (TransactionScope transaction = new TransactionScope())
                    {
    
                       //Do your operations here
                        transaction.Complete();
                        return "Mobile Brand Added";
    
                    }
                }
                catch (Exception ex)
                {
    
                    throw ex;
                }
            }