繁体   English   中英

使用SqlDataAdapter和环境事务锁定表丢失的触发器中的SQL Server错误

[英]SQL Server errors in trigger that locks table lost with SqlDataAdapter and ambient transaction

好的,所以我遇到了一个相当奇怪的情况。 我的情况有几个层次。 我还没有确定是否严格要求每一层,但这是正在发生的事情:

  • C#代码正在创建一个环境事务, SqlConnection自动登记到该事务中。
  • C#代码使用SqlDataAdapter将行插入表中。
  • InsertCommand引用存储过程。 存储过程是一个简单的INSERT语句。
  • INSERT正在进行的表中有一个触发器INSTEAD OF INSERT
  • 触发器在表上获得独占锁。
  • 触发器内发生错误。

使用此连接,C#代码中不会引发错误。 但是,如果触发器在表上获得独占锁定,则错误确实会影响C#代码。

错误实际发生的事情,不过,由一个事实,即在SQL Server端,该交易已中止证明。 C#代码不知道事务已中止,并且只在处理TransactionScope尝试COMMIT TRANSACTION时遇到错误。

我创建了这种场景的最小复制:

https://github.com/logiclrd/TestErrorWhileLockedInTrigger

有没有人理解为什么会这样,以及如何恢复正确的错误处理行为?

所以,我已经做了更多的测试。

我的第一个想法是,如果持有独占锁是导致它压制错误,也许明确释放锁将会解开它吗? 于是,我把一个TRY / CATCH围绕在我的证明了概念产生错误的语句,有它ROLLBACK TRANSACTION ,然后重新THROW ,但它没有做任何事情。

那么我的下一个想法是, RAISERROR语句在使用严重级别为20-25时强制终止连接。 我不确定这是否是一个理想的解决方案,因为它会在发生这种情况时写入SQL Server事件日志的条目。 但是,它确实实现了让SqlDataAdapter在其Update命令期间看到错误而不是C#代码的目标,认为事务仍处于活动状态并尝试提交它。

有没有人知道这种“大锤”方法的其他潜在缺点,或者它是否可能成为在这种情况下错误传播的唯一方法?

我已经确定了问题的原因。

锁定表的触发器中的语句如下所示:

SELECT TOP 0 *
  FROM TableToTriggerAndLock WITH (TABLOCKX, HOLDLOCK)

虽然这不返回任何数据,但它确实返回(空)结果集。 事实证明, SqlDataAdapter类只关心它在TDS流上返回的第一个结果集,因此第二个结果集中返回的错误完全被传递。

取出锁定语句,然后取出冗余结果集,现在错误出现在第一个结果集中。

那么,解决方案是抑制结果集,我通过将锁定语句重新处理为:

DECLARE @Dummy INT

SELECT TOP 0 @Dummy = 1
  FROM TableToTriggerAndLock WITH (TABLOCKX, HOLDLOCK)

希望这可以帮助那些使用SqlDataAdapter和更复杂的底层操作的人。 :-)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM