Зачем вообще вызывать метод SqlClient.SqlDataReader Close()?

Является ли 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.


person Julius A    schedule 30.10.2008    source источник


Ответы (8)


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

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

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

person wprl    schedule 30.10.2008
comment
Следует упомянуть параметр параметра CommandBehavior.CloseConnection, который устраняет задержку между удалением и фактической сборкой мусора, когда соединение будет выпущено в пул ADO. - person Charles Bretana; 22.08.2012
comment
GC не вызывает Dispose, так что не рассчитывайте на это. Он вызывает Finalize. - person Crowcoder; 28.07.2015

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

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

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

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

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

person FlySwat    schedule 30.10.2008
comment
Это твое правило звучит хорошо и элегантно - я должен одолжить для своего магазина - person Julius A; 30.10.2008
comment
Интересно. Я поиграю с этим и посмотрю, будет ли это работать. Во всяком случае, мне это нравится намного больше. - person Lieutenant Frost; 30.10.2008
comment
Есть ли здесь какая-либо польза от блока использования для DataReader или достаточно блока использования для подключения? например используя (IDataReader dr = command.ExecuteReader()) - person Seth Reno; 25.07.2011
comment
и что, если читатель не может быть создан с помощью блока, например. он передается как выходной параметр или используется повторно или ...? - person bjan; 11.05.2012
comment
Слишком много использований = слишком много путаницы, ИМХО. Но, конечно, у каждого могут быть свои предпочтения. И, как уже отмечалось, бывают случаи, когда использовать оператор using нельзя. - person ThunderGr; 13.12.2012

Одна из хороших практик (если вы не используете соединения повторно) состоит в том, чтобы добавить Command Behavior в SqlDataReader, чтобы закрыть соединение, когда оно будет удалено:

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

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

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

person Jeffrey Harrington    schedule 30.10.2008

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

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

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

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

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

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

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

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

person Will Rickards    schedule 30.10.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, если вы хотите, чтобы ваш метод немедленно освобождал соединение обратно в пул, когда вы закончите с Это...

person Charles Bretana    schedule 30.10.2008

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

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

person Joel Coehoorn    schedule 30.10.2008

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

person James Curran    schedule 30.10.2008

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

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

person Lieutenant Frost    schedule 30.10.2008
comment
Конструкция «использование» более элегантна и гарантирует, что объект будет удален с той же степенью уверенности, что и «наконец». - person Joel Coehoorn; 30.10.2008
comment
@ Джоэл, что более элегантно, это, безусловно, вопрос личного вкуса. Поскольку оба метода будут располагать объект с «одинаковой степенью достоверности». Лично я предпочитаю полагаться на код, который рассказывает, что делает. Оператор using не сообщает, что он удаляет и что удаляет, но в операторе finally вы можете быть уверены, что удаляете то, что там написано, и ничего больше или меньше. Предположения — корень многих проблем. - person ThunderGr; 13.12.2012
comment
@ThunderGr Я должен согласиться с Джоэлом. Оператор using сообщает вам, что он удаляет и что он удаляет, вам просто нужно понять, как операторы использования работают в C#, что, вероятно, является хорошей идеей, если вы работаете в C#. - person Adam Davis; 25.08.2019