Как правильно обеспечить закрытие соединения SQL при возникновении исключения? - PullRequest
18 голосов
/ 26 сентября 2008

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

Конкретно мне интересно; в случае, если выброшено исключение, достаточно ли кода, который у меня есть в блоке 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 ]

45 голосов
/ 26 сентября 2008

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

using (SqlConnection conn = new SqlConnection (...))
{
    // Whatever happens in here, the connection is 
    // disposed of (closed) at the end.
}
8 голосов
/ 26 сентября 2008

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

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

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

5 голосов
/ 26 сентября 2008

Документы MSDN проясняют это ...

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

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


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

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


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

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


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

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

2 голосов
/ 26 сентября 2008

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

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

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

1 голос
/ 26 сентября 2008

См. Этот вопрос для ответа:

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

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

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

1 голос
/ 26 сентября 2008

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

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

1 голос
/ 26 сентября 2008

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

0 голосов
/ 06 февраля 2009

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

0 голосов
/ 26 сентября 2008

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


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

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

...