Подтвердить что ты не робот

Зачем вообще обращаться к методу 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

4b9b3361

Ответ 1

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

Дополнительная литература

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

Ответ 2

@Lieutenant Frost

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

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

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

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

Ответ 3

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

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

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

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

Ответ 4

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

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

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

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

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

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

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

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

Ответ 5

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

В зависимости от того, сколько времени прошло до запуска 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, когда вы хотите, чтобы ваш метод немедленно отпустил соединение в пул, когда вы делаются с ним...

Ответ 6

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

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

Ответ 7

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

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

Ответ 8

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