代码之家  ›  专栏  ›  技术社区  ›  Colin

调用ExecuteSqlCommand时如何正确处理CommandTimeout

  •  2
  • Colin  · 技术社区  · 6 年前

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

    0 回复  |  直到 6 年前