Класс .NET SqlConnection, логика пула соединений и переподключения

У нас есть некоторый клиентский код, который использует класс SqlConnection в .NET для связи с базой данных SQLServer. Это периодически терпит неудачу с этой ошибкой:

"ExecuteReader требует открытого и доступного соединения. Текущее состояние соединения закрыто"

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

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

Мы хотели бы переписать код, но прежде чем я сделаю это, мне нужно знать несколько вещей:

Мой первый вопрос: неэффективно ли повторно подключать и отключать объекты SqlConnection, или базовая библиотека выполняет пул соединений от нашего имени?

// Is this bad/inefficient?
for(many-times)
{
    using(SQLConnection conn = new SQLConnection(connectionString))
    {
        // do stuff with conn
    }
}

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

Если оказывается, что стоит «кэшировать» объекты SqlConnection, что является рекомендуемым способом обработки всех ошибок, которые могут быть устранены простым «переподключением» к базе данных. Я говорю о таких сценариях, как:

База данных переведена в автономный режим и снова включена, но клиентский процесс не имел открытых транзакций, пока это происходило База данных была «отключена», затем «подключена»

Я заметил, что в SqlConnection есть свойство "State" ... есть ли подходящий способ сделать запрос?

Наконец, у меня есть тестовый экземпляр SQLServer с полными правами доступа: как я могу воспроизвести точную ошибку "ExecuteReader требует открытого и доступного соединения. Текущее состояние соединения закрыто"

Ответы на вопрос(3)

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

Стоимость создания нового соединения «с нуля» значительна, потому что для аутентификации требуется несколько циклов между клиентом и сервером (в зависимости от метода аутентификации и настроек SSL это может быть 1 раунд в лучшем случае против около 10 в худшем случае).

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

 Anders Rune Jensen04 февр. 2013 г., 15:56
Обратите внимание, что если у вас включен пул соединений, и вы открываете соединение, чтобы проверить, существует ли база данных, а затем создайте базу данных на основе этой информации. Тогда трюк sp_reset_connection не позволит вам увидеть базу данных. Так что сделайте первое соединение без пула соединений.
 Richard29 июн. 2009 г., 16:35
о умолчанию пул @Connection, вам не нужно ничего делать, чтобы включить его. Вы можете отключить его, но это редкое требование.
Решение Вопроса

SqlConnection объекты и закройте каждый из них, когда вы закончите. Это именно то, что нужно сделать. Пусть пул соединений .NET Framework сделает свое дело - не пытайтесь делать это самостоятельно. Вам не нужно делать ничего особенного, чтобы включить пул соединений (хотя вы можете отключить его, установивPooling=false в строке подключения).

Есть много вещей, которые могут пойти не так, если вы попытаетесь кэшировать соединение самостоятельно. Просто сказать нет :

 Richard29 июн. 2009 г., 16:34
Чтобы отключить пул соединений: добавьте "Pooling = False;" к строке подключения.
 Jon Skeet29 июн. 2009 г., 16:58
@ Ричард: Ага, я добавил это за несколько минут до твоего комментария
 cmart17 дек. 2015 г., 11:31
@ JonSkeet Извините, что тыкаю в старую тему. Но как насчет стоимости GC? Я имею в виду, что даже если базовое соединение SQL объединено в пул (однако это соединение, вероятно, сбрасывается при его утилизации, которое также делает вызовы в DB: sp_reset_connection), созданные экземпляры SqlConnection также должны быть GC'd. Так почему бы не поместить цикл в блок использования и сохранить сброс соединения и затраты на сборку мусора? Единственная причина, о которой я мог подумать, - это возможная конкретная ситуация безопасности транзакций для каждой операции БД. Так о чем еще вы думали по поводу своего ответа еще в 2009 году? ; -)
 Jon Skeet29 июн. 2009 г., 16:31
Трудно сказать, если честно. Это может быть просто условие гонки, если вы пытаетесь использовать одно и то же соединение из нескольких потоков.
 Paul Hollingsworth29 июн. 2009 г., 16:25
Что я надеялся услышать ... Какие-нибудь идеи о том, как я могу воспроизвести точную ошибку, которую мы видим? (просто чтобы "доказать", что я исправил проблему)?

using(SQLConnection conn = new SQLConnection(connectionString))
{
    // do stuff with conn
}

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

Ваш ответ на вопрос