Как правильно обрабатывать CommandTimeout при вызове ExecuteSqlCommand - PullRequest
0 голосов
/ 12 июня 2018

У меня есть несколько долго выполняющихся команд в хранимых процедурах, которые подвергаются риску истечения времени ожидания, и я запускаю их, используя Context.Database.ExecuteSqlCommand

Может показаться, что когда команда истекает, она оставляет блокировку вбазы данных, потому что транзакция не откатывается.

Я нашел объяснение этому здесь: CommandTimeout - как правильно его обрабатывать?

На основе связанных пример Я изменил свой код на:

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

После комментариев от Эннии 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 сообщает мне идентификатор сеанса, и когда я проверяю свои блокировки, используя этот запрос: SELECT * FROM sys.dm_tran_locks, я нахожу совпадение для идентификатора сеанса в request_session_id, но это уже была блокировка, а не одна из новыхте, так что я немного запутался.

Итак, как мне правильно обрабатывать CommandTimeout при использовании ExecuteSqlCommand, чтобы гарантировать немедленное снятие блокировок?

Я скачал sp_whoisactive изапустив его, spid, кажется, связан с запросом к таблицам, используемым Hangfire - я использую Hangfire для запуска длительных запросов в фоновом процессе.Итак, я думаю, что, возможно, я лаю не на том дереве.У меня действительно была проблема с блокировкой, но я переписал свои собственные запросы, чтобы избежать блокировки слишком большого количества строк, и я отключил повышение блокировки для таблиц, где у меня возникла проблема.Эти последние блокировки могут исходить от Hangfire, и они могут быть несущественными, тем не менее, я решил пока использовать XACT_ABORT ON.

...