【问题标题】:SQL Server errors in trigger that locks table lost with SqlDataAdapter and ambient transaction使用 SqlDataAdapter 和环境事务锁定表丢失的触发器中的 SQL Server 错误
【发布时间】:2017-08-14 21:12:39
【问题描述】:

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

  • C# 代码正在创建环境事务,SqlConnection 会自动加入其中。
  • C# 代码正在使用SqlDataAdapter 向表中插入一行。
  • InsertCommand 正在引用存储过程。存储过程是一个简单的INSERT 语句。
  • 正在执行INSERT 的表上有一个触发器INSTEAD OF INSERT
  • 触发器在表上获得排他锁。
  • 触发器中出现错误。

使用此连接,C# 代码中不会引发错误。但是,如果触发器没有在表上获得排他锁,则错误确实由 C# 代码引起。

错误实际上发生了,但事实证明,在 SQL Server 端,事务已中止。 C# 代码不知道事务已被中止,只有当TransactionScope 尝试处理COMMIT TRANSACTION 时才会遇到错误。

我已经创建了这个场景的最小复制:

https://github.com/logiclrd/TestErrorWhileLockedInTrigger

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

【问题讨论】:

  • 任何看过这个并被Database Initialization.sql 和代码之间的不匹配搞糊涂的人,那是我的错。在添加和提交到 git 之前,我忘了打 Save。我已经使用正确的初始化代码进行了后续提交。

标签: c# sql-server error-handling transactions transactionscope


【解决方案1】:

所以,我对此进行了更多测试。

我的第一个想法是,如果持有独占锁会导致它消除错误,那么显式释放锁可能会消除它吗?所以,我在我的概念验证中生成错误的语句周围加上了一个TRY/CATCH,有它ROLLBACK TRANSACTION,然后重新THROW,但它没有做任何事情。

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

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

【讨论】:

    【解决方案2】:

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

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

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

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

    取出加锁语句,取出那个多余的结果集,现在错误在first结果集中。

    因此,解决方案是抑制结果集,我通过将锁定语句改写为:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-26
      • 2019-06-30
      • 1970-01-01
      相关资源
      最近更新 更多