我在存储过程中有一些长时间运行的命令有超时的风险,我使用
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;
}
但是,这在捕获中引发了异常,因为连接现在为空:
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
一个非常小的值来测试它。
我在抛出处放置了一个断点,因此我知道事务已回滚。debug变量告诉我会话id,当我使用此查询检查锁时:
SELECT * FROM sys.dm_tran_locks
,我在
request_session_id
,但是已经有锁了,不是新的,所以我有点困惑。
那么,我该如何处理
命令超时
使用时
ExecuteSqlCommand
以确保锁被立即释放?
我下载了
sp_whoisactive
然后运行它,spid似乎链接到Hangfire使用的表上的查询-我使用Hangfire在后台进程中运行长时间运行的查询。所以,我想也许我找错了方向。我确实在锁定方面有问题,但是我重写了自己的查询以避免锁定太多的行,并且在有问题的表上禁用了锁升级。这些最后的锁可能来自飞机失火,可能并不重要,尽管如此,我还是决定
XACT_ABORT ON
现在。