Каков надлежащий способ обеспечить, чтобы соединение с 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();
    }
}

Ответ 1

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

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

Ответ 2

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

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

Высокоразвитые "решения", как правило, приводят к ошибочному коду. Простой лучше.

Ответ 3

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

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

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


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

Итак, зачем тестировать Closed? Просто вызовите Close().


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

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


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

Важная подсказка. Спасибо, Эгон.

Ответ 4

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

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

Одна вещь, которую вы обязательно должны изменить, это метод Dispose(). Вы не должны ссылаться на объект соединения в распоряжении, потому что он, возможно, уже был финализирован в этой точке. Вместо этого вы должны следовать рекомендуемому шаблону Dispose.

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

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

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

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

Ответ 8

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

Ответ 9

Могу я предложить следующее:


    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();
        }
    }

Надеюсь, что помогает:)