Как правильно обеспечить закрытие соединения 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();
}
}
Надеюсь, это поможет:)