Зачем вызывать метод SqlClient.SqlDataReader Close ()? - PullRequest
17 голосов
/ 30 октября 2008

Является ли SqlClient.SqlDataReader управляемым объектом .NET или нет? Почему мы должны вызывать метод Close (), чтобы явно закрыть открытое соединение? Разве не должен выходить из области видимости для такого объекта автоматически закрыть это? Разве сборщик мусора не должен его убирать?

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

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

Exception: System.InvalidOperationException
Message: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
Source: System.Data 

at  System.Data.SqlClient.SqlConnectionPoolManager.GetPooledConnection(SqlConnectionString options, Boolean& isInTransaction)
at  System.Data.SqlClient.SqlConnection.Open()

Чтобы это исправить, мне пришлось явно закрыть все объекты SQLDataReader.

Я использую .NET Framework 3.5

Ответы [ 8 ]

35 голосов
/ 30 октября 2008

Конечно, он будет собран, когда выйдет за рамки (если на него нет других ссылок). Когда он будет собран, он будет закрыт с помощью метода Dispose (). Тем не менее, вы никогда не знаете, когда GC собирается освободить вещи; если вы не закроете свои читатели, у вас очень быстро закончатся доступные подключения к базе данных.

Дальнейшее чтение

~ Уильям Райли-Лэнд

17 голосов
/ 30 октября 2008

@ Лейтенант Фрост

Как правило, в нашем магазине мы явно обернуть все вызовы базы данных в Попробуй ... Наконец-то блок, наконец-то раздел ловить и закрывать данные соединения. Это стоит чуть-чуть усилий, чтобы сохранить себя главным устранение головной боли.

У меня есть похожее правило, но я требую, чтобы объекты, реализующие IDisposable, использовали блок 'using'.

using (SqlConnection conn = new SqlConnection(conStr))
{
     using (SqlCommand command = new SqlCommand())
     {
        // ETC
     } 
}

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

10 голосов
/ 30 октября 2008

Хорошей практикой (если вы не используете соединения повторно) является добавление командного поведения в SqlDataReader для закрытия соединения при его удалении:

SqlDataReader rdr = cmd.ExecuteReader( CommandBehavior.CloseConnection );

Добавление этого гарантирует, что соединение с базой данных будет закрыто, когда объект SqlDataReader закрыт (или сборщик мусора).

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

4 голосов
/ 30 октября 2008

Я думаю, что все остальные говорили это, но я хотел, чтобы это было ясно:

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

Ваше приложение должно «хорошо играть» на нескольких уровнях. Закрытие соединений поможет вам сделать это. Давайте рассмотрим некоторые из этих уровней.

1: Вы не полагаетесь на сборку мусора. В идеале, сборка мусора не должна существовать. Но это так. Но, несомненно, вы не должны на это полагаться.

2: вы не удерживаете соединения с базой данных. В то время как соединения обычно объединяются, как вы обнаружили, существует ограничение. Если держать это дольше, чем нужно, ваше приложение станет плохим яблоком.

3: Вы не генерируете сетевой трафик. Каждое соединение с базой данных по сути является TCP-соединением. Держа его открытым, вероятно, генерирует сетевой трафик по направлениям Ты еще там? да. Небольшой трафик да, но в многолюдной сети это плохая практика. И сам SQL Server использует ресурсы для поддержания вашего соединения живым. Ресурсы, которые другие люди пытаются получить на этом сервере sql, могли бы лучше использовать.

Размышляя о доступе к базе данных, вы также должны думать о сетевых ресурсах. Некоторые способы получить данные плохи, потому что они могут принести ненужные вещи для поездки. Более ранние версии ADO были довольно печально известны для такого рода вещей. Возвращение информации о схеме, когда вы просто хотите получить данные. Конечно, с несколькими подключениями это не проблема. Но с каких пор любая база данных имеет только несколько соединений. Так что подумайте об этом и постарайтесь не злоупотреблять ресурсами.

Если в вашем веб-приложении всего 100 пользователей, вас это может не волновать. Но как насчет 100 000? Всегда учитывайте, что происходит при масштабировании.

3 голосов
/ 30 октября 2008

Если вы явно не закроете его, то он будет сидеть и ждать, пока сборщик мусора не "соберет" его ... Только после этого он возвращается в пул подключений ADO.Net для повторного использования другим Соединение. Открытый запрос, поэтому в течение всего прошедшего времени любые другие запросы на соединение должны будут создавать совершенно новые, даже если там есть совершенно хороший, неиспользованный, который можно использовать ...

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

Но в методе Command.ExecuteReader () есть необязательный параметр, называемый CommandBehavior. Это перечисление со значениями: CommandBehavior.Default, CommandBehavior.SingleResult, CommandBehavior.SchemaOnly, CommandBehavior.KeyInfo, CommandBehavior.SingleRow, CommandBehavior.SequentialAccess и CommandBehavior.CloseConnection

Это перечисление имеет атрибут FlagsAttribute, который допускает побитовую комбинацию значений его членов. Это последнее значение (CommandBehavior.CloseConnection), которое имеет отношение здесь. Он сообщает объекту Command закрыть соединение, когда читатель данных закрыт. http://msdn.microsoft.com/en-us/library/system.data.commandbehavior.aspx

К сожалению, по умолчанию НЕ закрывается соединение, когда читатель данных закрыт, но вы можете (и должны) передать этот параметр как CommandBehavior.CloseConnection, если хотите, чтобы ваш метод немедленно освободил соединение обратно в пул, когда вы с этим покончено ...

2 голосов
/ 30 октября 2008

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

Как правило, в нашем магазине мы явно упаковываем все вызовы базы данных в блок Try ... finally с разделом finally, перехватывающим и закрывающим соединения данных. Стоит потратить немало усилий, чтобы избавить себя от головной боли при устранении неполадок.

2 голосов
/ 30 октября 2008

«Управляемый» ресурс, называемый термином «управляемый код», - это память. Вот и все. Любой другой дефицитный ресурс необходимо обернуть одноразовым шаблоном, включая соединения с базой данных.

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

1 голос
/ 30 октября 2008

Проблема не в соединении, а в том, что SQL-курсор удерживается SqlDataReader. Если вы попытаетесь открыть секунду, не закрывая первую, она выдаст исключение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...