从.NET 3.5 / C#应用程序中,我想捕获SqlException
,但只有在SQL Server 2008实例上由
典型错误消息为Transaction (Process ID 58) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
然而,对于此异常,似乎没有记录error code。
在消息中针对 deadlock 关键字的存在过滤异常似乎是实现此行为的一种非常难看的方式。有人知道这样做的正确方法吗?
答案 0 :(得分:140)
针对死锁的Microsft SQL Server特定错误代码是1205,因此您需要处理SqlException并检查它。所以,例如如果对于所有其他类型的SqlException,您希望气泡异常:
catch (SqlException ex)
{
if (ex.Number == 1205)
{
// Deadlock
}
else
throw;
}
或者,使用C#6中提供的异常过滤
catch (SqlException ex) when (ex.Number == 1205)
{
// Deadlock
}
查找给定消息的实际SQL错误代码的一个方便的事情是查看SQL Server中的sys.messages。
e.g。
SELECT * FROM sys.messages WHERE text LIKE '%deadlock%' AND language_id=1033
处理死锁(来自SQL Server 2005及更高版本)的另一种方法是使用TRY ... CATCH支持在存储过程中执行此操作:
BEGIN TRY
-- some sql statements
END TRY
BEGIN CATCH
IF (ERROR_NUMBER() = 1205)
-- is a deadlock
ELSE
-- is not a deadlock
END CATCH
MSDN中有一个完整的示例here,说明如何在SQL中实现死锁重试逻辑。
答案 1 :(得分:40)
因为我想你可能想要检测死锁,为了能够重试失败的操作,我想警告你一点点陷阱。我希望你能原谅我在这里有点偏离主题。
数据库检测到的死锁将有效地回滚您正在运行的事务(如果有),而连接在.NET中保持打开状态。重试该操作(在同一连接中),意味着它将在无事务处理的上下文中执行,这可能导致数据损坏。
重要的是要意识到这一点。最好考虑在SQL导致失败的情况下注定完整的连接。重试操作只能在定义事务的级别上完成(通过重新创建该事务及其连接)。
因此,当您重试失败的操作时,请确保打开一个全新的连接并开始新的交易。
答案 2 :(得分:2)
这是一种检测死锁的C#6方法。
try
{
//todo: Execute SQL.
//IMPORTANT, if you used Connection.BeginTransaction(), this try..catch must surround that code. You must rollback the original transaction, then recreate it and re-run all the code.
}
catch (SqlException ex) when (ex.Number == 1205)
{
//todo: Retry SQL
}
确保此try..catch围绕整个交易。根据@Steven(详见其答案),当sql命令因死锁而失败时,会导致事务回滚,如果不重新创建事务,则重试将在上下文之外执行该事务可能导致数据不一致。