SQL 锁定 table 的触发器中的服务器错误与 SqlDataAdapter 和环境事务一起丢失

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

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

有了这个结合,错误就不会在 C# 代码中出现。但是,如果触发器 没有 获得 table 上的独占锁,则错误确实弥补了 C# 代码。

错误 实际发生的,但事实证明,在 SQL 服务器端,事务已中止。 C#代码并不知道事务已经中止,只有在 TransactionScope 的处理尝试 COMMIT TRANSACTION.

时才会遇到错误

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

https://github.com/logiclrd/TestErrorWhileLockedInTrigger

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

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

我的第一个想法是,如果持有独占锁导致它压制错误,也许显式释放锁会解除压制?所以,我在我的概念验证中生成错误的语句周围放置了一个 TRY/CATCH,让它 ROLLBACK TRANSACTION 然后重新 THROW,但是它什么都没做。

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

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

我已经确定问题的原因。

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

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

虽然此 return 没有数据,但它 确实 return 一个(空)结果集。事实证明 SqlDataAdapter class 只关心它在 TDS 流上返回的第一个结果集,因此 second 结果集中返回的错误是完全过去了。

把locking语句去掉,你把那个多余的结果集去掉,现在错误在第一个个结果集中

然后,解决方案是抑制结果集,我通过将锁定语句修改为:

DECLARE @Dummy INT

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

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