我有一个更新我的业务实体的例程。更新涉及大约6个不同的表。所有命令都在事务中执行。
最近,我需要在例程中添加一些代码来访问数据库中的查找表。查找代码已经存在于另一个业务对象中,因此我使用了该业务对象。例如:
Using tr As DbTransaction = myConnection.BeginTransaction()
ExecuteCommand1(tr)
ExecuteCommand2(tr)
If myLookupTable.GetLookupTable().FindById(id).HasFlagSet Then
ExecuteCommand3(tr)
End If
End Using
但是,查找表业务对象挂起/死锁。我认为这是因为它没有对原始例程使用的事务的引用。
在做了一些研究之后,我尝试将查找表逻辑放在自己的事务中,将IsolationLevel
设置为ReadUncommitted
。这给了我想要的结果。然而,经过进一步的研究,我现在正在猜测我是否已经正确实现了这一点。
假设我的查找表对象无法使用对活动事务的引用,那么我所描述的最佳实践是什么?我觉得我可能会遗漏一些东西。
答案 0 :(得分:3)
如果您正在事务中间进行读取,那么您应该在事务上下文中执行此操作,而不是使用不同的事务和脏读操作。幸运的是,有一个简单的解决方案:使用.Net TransactionScope对象,而不是使用ADO.Net事务对象。 ADO.Net代码对它是明智的,并将在此事务中包含您的所有操作,包括您的其他业务组件读取。只需确保您的业务对象没有打开不同的连接,这将导致尝试将现有事务升级为分布式事务并将新连接登记到其中。
另一种方法是在每次调用时传递你的SqlConnection / SqlTransaction对,但是在你的代码中到处都传播可怕的丑陋。
答案 1 :(得分:0)
如果是我,我会重写逻辑,所以我不必做一个未提交的读取。
答案 2 :(得分:0)
避免死锁的黄金法则是在每个事务中始终以相同的顺序获取表锁。所以看看其他事务中的代码,看看他们采取表锁的顺序;然后确保在交易中使用相同的订单。
答案 3 :(得分:0)
显然你的查找试图访问由事务tr独占锁定的行。如果您使用readuncommitted事务或在查询查询中使用WITH(NOLOCK),您将看到可能正在发生的事务的所有未提交的更改并影响您的查找逻辑。所以我不太确定这会是多么可取。
我认为如果您需要在该事务期间进行查找,最好找到一种方法来确保您的查询查询参与当前事务。如果要在同一个线程中执行所有这些操作,那么您可以做的一件事是在创建一个事务对象时将事务对象存储在线程本地存储中,并让GetLookupTable方法检查事务对象的线程本地存储,如果有的话是一个事务集,您可以从该事务对象获取连接。否则,您将创建一个新连接。这样,您的查找将成为该事务的一部分,它应该运行其逻辑而不会被当前事务阻塞,从而阻止当前事务,从而导致死锁。