调用ExecuteSqlCommand时

时间:2018-06-12 11:59:44

标签: sql-server entity-framework entity-framework-6 azure-sql-database hangfire

我在存储过程中有一些长时间运行的命令存在超时风险,我使用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

0 个答案:

没有答案