我在存储过程中有一些长时间运行的命令存在超时风险,我使用Context.Database.ExecuteSqlCommand
运行它们
当命令超时时,它会在数据库中保留一个锁,因为事务没有回滚。
我在这里找到了解释:CommandTimeout – How to handle it properly?
根据链接example,我将代码更改为:
Database database = Context.Database;
try
{
return database.ExecuteSqlCommand(sql, parameters);
}
catch (SqlException e)
{
//Transactions can stay open after a CommandTimeout,
//so need to rollback any open transactions
if (e.Number == -2) //CommandTimeout occurred
{
//Single rollback exits all levels of nested transactions,
//no need to loop.
database.ExecuteSqlCommand("IF @@TRANCOUNT>0 ROLLBACK TRAN;");
}
throw;
}
但是,这会在catch中引发异常,因为连接现在为null:
ArgumentNullException: Value cannot be null.
Parameter name: connection
根据Annie和usr的评论,我将代码更改为:
Database database = Context.Database;
using (var tran = database.BeginTransaction())
{
try
{
int result = database.ExecuteSqlCommand(sql, parameters);
tran.Commit();
return result;
}
catch (SqlException)
{
var debug = database.SqlQuery<Int16>("SELECT @@SPID");
tran.Rollback();
throw;
}
}
我真的以为会这样做,但是当我将CommandTimeout
设置为一个非常小的值来测试它时,数据库中的锁会继续累积。
我在抛出一个断点,所以我知道该事务已被回滚。调试变量告诉我会话ID以及当我使用此查询检查我的锁时:SELECT * FROM sys.dm_tran_locks
,我在request_session_id
中找到了会话ID的匹配项,但它是已经存在的锁,而不是其中一个新的,所以我有点困惑。
那么,在使用CommandTimeout
确保锁定立即释放时,我应该如何正确处理ExecuteSqlCommand
?
我下载了sp_whoisactive
并运行它,spid似乎链接到Hangfire使用的表上的查询 - 我正在使用Hangfire在后台进程中运行长时间运行的查询。所以,我想也许我正在咆哮错误的树。我确实遇到了锁定问题,但我已经重写了自己的查询以避免锁定太多行,并且我已经在我遇到问题的表上禁用了锁升级。这些最后的锁可能来自Hangfire,可能不是很重要,但我现在决定使用XACT_ABORT ON
。