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

StackOverflow https://stackoverflow.com/questions/141204

  •  02-07-2019
  •  | 
  •  

Вопрос

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

В частности, мне интересно;в случае возникновения исключения достаточно ли кода, который у меня есть в блоке 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();
    }
}
Это было полезно?

Решение

Оберните код обработки вашей базы данных в поле "using".

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 более одного раза.Исключение не генерируется.

Так зачем утруждать себя тестированием на Закрытость?Просто вызовите Close() .


  • Закрыть и Утилизировать функционально эквивалентны.

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


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

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

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

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

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

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

Смотрите ответ на этот вопрос:

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

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

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

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

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

нет необходимости пробовать .. наконец, вокруг "использования", использования ЯВЛЯЕТСЯ попробовать...наконец-то

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


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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top