Как правильно обеспечить закрытие соединения SQL при возникновении исключения?

Я часто использую шаблон, который выглядит примерно так. Мне интересно, все ли в порядке или есть лучшая практика, которую я здесь не применяю.

Конкретно мне интересно; в случае, если выброшено исключение, достаточно ли кода, который у меня есть в блоке finally, чтобы обеспечить надлежащее закрытие соединения?

public class SomeDataClass : IDisposable
{
    private SqlConnection _conn;

    //constructors and methods

    private DoSomethingWithTheSqlConnection()
    {
        //some code excluded for brevity

        try
        {
            using (SqlCommand cmd = new SqlCommand(SqlQuery.CountSomething, _SqlConnection))
            {
                _SqlConnection.Open();
                countOfSomething = Convert.ToInt32(cmd.ExecuteScalar());
            }
        }
        finally
        {
            //is this the best way?
            if (_SqlConnection.State == ConnectionState.Closed)
                _SqlConnection.Close();
        }

        //some code excluded for brevity
    }

    public Dispose()
    {
        _conn.Dispose();
    }
}

9 ответов

Решение

Оберните ваш код обработки базы данных в "использование"

using (SqlConnection conn = new SqlConnection (...))
{
    // Whatever happens in here, the connection is 
    // disposed of (closed) at the end.
}

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

Вы можете просто использовать оператор using и быть уверенным, что IDBConnection.Release() закроет для вас соединение.

Тщательно продуманные "решения" приводят к ошибочному коду. Просто лучше.

Документы MSDN ясно дают понять...

  • Метод Close откатывает все ожидающие транзакции. Затем он освобождает соединение с пулом соединений или закрывает соединение, если пул соединений отключен.

Возможно, вы не отключили пул соединений (и не хотите его отключать), поэтому в конечном итоге пул управляет состоянием соединения после вызова "Закрыть". Это может быть важно, так как вы можете смущаться, глядя со стороны сервера базы данных на все открытые соединения.


  • Приложение может вызывать Close более одного раза. Никаких исключений не генерируется.

Так зачем беспокоиться о тестировании на Closed? Просто позвоните Close ().


  • Close и Dispose функционально эквивалентны.

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


  • Не вызывайте Close или Dispose для Connection, DataReader или любого другого управляемого объекта в методе Finalize вашего класса.

Важный совет по безопасности. Спасибо, Эгон.

Я предполагаю, что под "_SqlConnection.State == ConnectionState.Closed" вы имели в виду!=.

Это, безусловно, будет работать. Я думаю, что более привычно содержать сам объект соединения внутри оператора using, но то, что у вас есть, хорошо, если вы хотите повторно использовать тот же объект соединения по какой-то причине.

Тем не менее, вы должны определенно изменить метод Dispose(). Вы не должны ссылаться на объект подключения в dispose, потому что он, возможно, уже был завершен в тот момент. Вы должны следовать рекомендованному шаблону Dispose.

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

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

Смотрите этот вопрос для ответа:

Закрыть и утилизировать - куда звонить?

Если время жизни вашего соединения - один вызов метода, используйте using особенность языка для обеспечения правильной очистки соединения. В то время как try/finally Блок функционально одинаков, требует больше кода, а ИМО менее читабелен. Нет необходимости проверять состояние соединения, вы можете позвонить Dispose независимо, и он будет заниматься очисткой соединения.

Если время жизни вашего соединения соответствует времени жизни содержащего класса, то реализуйте IDisposable и очистить соединение в Dispose,

Так как вы используете IDisposables в любом случае. Вы можете использовать ключевое слово using, которое в основном эквивалентно вызову dispose в блоке finally, но выглядит лучше.

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

Могу ли я предложить это:


    class SqlOpener : IDisposable
    {
        SqlConnection _connection;

        public SqlOpener(SqlConnection connection)
        {
            _connection = connection;
            _connection.Open();

        }

        void IDisposable.Dispose()
        {
            _connection.Close();
        }
    }

    public class SomeDataClass : IDisposable
    {
        private SqlConnection _conn;

        //constructors and methods

        private void DoSomethingWithTheSqlConnection()
        {
            //some code excluded for brevity
            using (SqlCommand cmd = new SqlCommand("some sql query", _conn))
            using(new SqlOpener(_conn))
            {
                int countOfSomething = Convert.ToInt32(cmd.ExecuteScalar());
            }
            //some code excluded for brevity
        }

        public void Dispose()
        {
            _conn.Dispose();
        }
    }

Надеюсь, это поможет:)

Другие вопросы по тегам