[c#] Как получить эффективную обработку блокировки сервера Sql в C # с помощью ADO?



1 Answers

Основываясь на ответе @ Sam, я представляю метод обертки общего назначения:

private static T Retry<T>(Func<T> func)
{
    int count = 3;
    TimeSpan delay = TimeSpan.FromSeconds(5);
    while (true)
    {
        try
        {
            return func();
        }
        catch(SqlException e)
        {
            --count;
            if (count <= 0) throw;

            if (e.Number == 1205)
                _log.Debug("Deadlock, retrying", e);
            else if (e.Number == -2)
                _log.Debug("Timeout, retrying", e);
            else
                throw;

            Thread.Sleep(delay);
        }
    }
}

private static void Retry(Action action)
{
    Retry(() => { action(); return true; });
}

// Example usage
protected static void Execute(string connectionString, string commandString)
{
    _log.DebugFormat("SQL Execute \"{0}\" on {1}", commandString, connectionString);

    Retry(() => {
        using (SqlConnection connection = new SqlConnection(connectionString))
        using (SqlCommand command = new SqlCommand(commandString, connection))
            command.ExecuteNonQuery();
    });
}

protected static T GetValue<T>(string connectionString, string commandString)
{
    _log.DebugFormat("SQL Scalar Query \"{0}\" on {1}", commandString, connectionString);

    return Retry(() => { 
        using (SqlConnection connection = new SqlConnection(connectionString))
        using (SqlCommand command = new SqlCommand(commandString, connection))
        {
            object value = command.ExecuteScalar();
            if (value is DBNull) return default(T);
            return (T) value;
        }
    });
}
Question

У меня есть класс «База данных», который работает как оболочка для ADO.net. Например, когда мне нужно выполнить процедуру, я вызываю Database.ExecuteProcedure (procedureName, parametersAndItsValues).

Мы столкнулись с серьезными проблемами с ситуациями Deadlock в SQL Server 2000. Часть нашей команды работает над кодом и транзакциями sql для минимизации этих событий, но я думаю о том, чтобы сделать этот класс базы данных надежным в ситуациях взаимоблокировки.

Мы хотим, чтобы жертва тупика повторила, возможно, через некоторое время, но я не знаю, возможно ли это. Вот код для метода, который мы используем:

public int ExecuteQuery(string query)
{
    int rows = 0;

    try
    {
        Command.Connection = Connection;
        Command.CommandType = CommandType.Text;

        if(DatabaseType != enumDatabaseType.ORACLE)
          Command.CommandText = query;
        else
          Command.CommandText ="BEGIN " +  query + " END;";



        if (DatabaseType != enumDatabaseType.SQLCOMPACT)
            Command.CommandTimeout = Connection.ConnectionTimeout;

        if (Connection.State == ConnectionState.Closed)
            Connection.Open();

        rows = Command.ExecuteNonQuery();
    }
    catch (Exception exp)
    {
        //Could I add here any code to handle it?
        throw new Exception(exp.Message);
    }
    finally
    {
        if (Command.Transaction == null)
        {
            Connection.Close();
            _connection.Dispose();
            _connection = null;
            Command.Dispose();
            Command = null;
        }
    }
    return rows;
}

Могу ли я сделать эту обработку внутри блока catch?




Если у вас возникают проблемы с взаимоблокировками, было бы лучше посмотреть, что делает код SQL. Например, блокировка блокировки блокировки очень проста в создании, если у вас есть сериализуемый уровень изоляции (или что-то подобное в ваших rdbms) - и может быть смягчено несколькими способами, такими как повторный заказ запросов или (в SQL Server по крайней мере) с помощью (UPDLOCK) для блокировки записи раньше (так что вы не получаете конкурирующего чтения-блокировки).

Повторная попытка будет смешанной ... например, если вы находитесь в TransactionScope, она, возможно, уже прервана. Но только на уровне пуриста - если у меня возникают проблемы с разговором с db, я хочу, чтобы мой код паниковал, и панику рано ... Повторите попытку, кажется, немного взломанный в этом конкретном сценарии.




Related