读取Linq2SQL中的锁定

时间:2014-01-28 16:32:34

标签: linq-to-sql concurrency

我有一个经典的余额更新并发情况:

using (var context = GenerateContext())
{
   var account = context.Accounts.First(x => x.id == accountId);
   var balanceBefore = account.balance;
   // ... do some other stuff ...
   account.balance = balanceBefore + depositAmount;
   context.SubmitChanges();
}

我想在此更新期间锁定整个表以进行读/写。

可以用Linq2SQL完成吗?

修改

所以我尝试了以下操作,它会在事务期间锁定表,但它不会执行更新 - 平衡永远不会更改。

using (var context = GenerateContext())
{
   context.Connection.Open();
   context.Transaction = context.Connection.BeginTransaction(IsolationLevel.Serializable);
   var account = context.ExecuteQuery<Account>("SELECT TOP 1 * FROM [Account] WHERE [Id] = 10 WITH (TABLOCKX)").First();
   var balanceBefore = account.balance;
   // ... do some other stuff ...
   account.balance = balanceBefore + depositAmount;
   context.SubmitChanges();
}

我做错了什么?

2 个答案:

答案 0 :(得分:1)

首先,最好在后端处理余额更新:

UPDATE Account
  OUTPUT inserted.*
  SET balance += @deposit
  WHERE ID = @id;

但我们认为您只能在客户端上编写逻辑代码。您应该始终使用optimistic concurrency。在负载下乐观并发的好处太大而无法解雇。虽然悲观并发更容易编码(因为并发处理没有更新失败),但悲观并发在负载下表现不佳。

Linq2SQL支持乐观并发,请参阅Optimistic Concurrency: Overview。关键是Updatecheck属性。但是,应用程序中的处理并发冲突比检测它们更棘手,但主题完全取决于实际的应用程序逻辑。

对于极端罕见的情况,当乐观并发不合适时,第一个尝试的解决方案是......仍然乐观并发,但增加了application locks

如果您使用乐观并发并且您决定绝对必须采用悲观路线,那么即使世界将会结束,请使用(UPDLOCK, ROWLOCK)并在ID上拥有适当的索引。< / p>

答案 1 :(得分:0)

好的,我设法让它发挥作用。

我必须使用TransactionScope封装所有内容,并将TransactionOptions与隔离级别应用于事务范围。

现在看起来像这样:

using (var context = GenerateContext())
{
   var txnOptions = new TransactionOptions();
   txnOptions.IsolationLevel = System.Transactions.IsolationLevel.Serializable;
   using (var txnScope = new TransactionScope(TransactionScopeOption.Required, txnOptions))
   {  
       var account = context.ExecuteQuery<Account>("SELECT TOP 1 * FROM [Account] WITH (TABLOCKX)  WHERE [Id] = 10").First();
       var balanceBefore = account.balance;
       // ... do some other stuff ...
       account.balance = balanceBefore + depositAmount;
       context.SubmitChanges();
       txnScope.Complete();
   }
}