«Открыть / закрыть» SqlConnection или оставить открытым?

122

Моя бизнес-логика реализована в простых статических классах со статическими методами. Каждый из этих методов открывает / закрывает соединение SQL при вызове:

public static void DoSomething(string something)
{
    using (SqlConnection connection = new SqlConnection("..."))
    {
        connection.Open();

        // ...

        connection.Close();
    }
}

Но я думаю, что отказ от открытия и закрытия соединения снижает производительность . Давным-давно я провел несколько тестов с классом OleDbConnection (не уверен насчет SqlConnection), и это определенно помогло работать вот так (насколько я помню):

//pass the connection object into the method
public static void DoSomething(string something, SqlConnection connection)
{
    bool openConn = (connection.State == ConnectionState.Open);
    if (!openConn)
    {
        connection.Open();
    }

    // ....

    if (openConn) 
    {
        connection.Close();
    }
}

Итак, вопрос в том, что выбрать: метод (а) или метод (б)? Я читал в другом вопросе stackoverflow, что пул соединений сохранил для меня производительность, мне вообще не нужно беспокоиться ...

PS. Это приложение ASP.NET - соединения существуют только во время веб-запроса. Не беспроигрышное приложение или сервис.

Alex
источник
1
Просто совет: используйте DbConnection.StateChangeсобытие для отслеживания изменений в изменении состояния соединения (и может храниться локально) вместо DbConnection.Stateнепосредственной проверки свойства. Это сэкономит вам затраты на производительность.
дециклон
1
Не хватает одной детали: как этот метод является частью запроса страницы. Это единственный вызываемый метод или, как я предположил в своем ответе, один из многих методов, который вызывается при запросе страницы, он влияет на то, какой ответ правильный;)
Дэвид Мартенссон
Дэвид - МНОГО таких методов вызывается :)
Alex
1
Случай A показывает отсутствие веры в Dispose: см. Stackoverflow.com/questions/1195829/… и пример на MSDN msdn.microsoft.com/en-us/library/…
user2864740

Ответы:

82

Придерживайтесь варианта а .

Пул соединений - ваш друг.

Адриан Стандер
источник
37
ИМХО - он даже близко не должен. утилизация сделает это.
Рой Намир 07
2
@RoyiNamir Мне нравится призыв закрыть соединение. Специально для новичков и новичков в кодовой базе. Он более понятный и читаемый.
edhedges
27
@edhedges Использование как «using», так и Close () в конечном итоге только вызовет путаницу у новичков. Они не собираются понимать цель использования «использования». Не используйте «Закрыть» вместо того, чтобы научить их цели «использовать». Чтобы они могли учиться лучше и применять полученные знания к другим частям кода.
Луис Перес,
1
Нужно ли / нужно ли вызывать "Open ()"? В настоящее время я использую его так: using (var conn = GetConnection ()) {} public SqlConnection GetConnection () {return new SqlConnection (_connectionString); }
ganders
79

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

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

WeNeedAnswers
источник
34

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

Это становится более сложным, если вы сталкиваетесь с проблемами MSDTC, используя одну транзакцию вокруг кода, который использует несколько подключений, и в этом случае вам действительно нужно предоставить общий доступ к объекту подключения и закрыть его только после завершения транзакции.

Однако здесь вы все делаете вручную, поэтому вы можете изучить инструменты, которые управляют подключениями за вас, например DataSets, Linq to SQL, Entity Framework или NHibernate.

Нил Барнуэлл
источник
Вы не должны нормально открывать и закрывать соединение при каждом вызове метода, только один раз для каждого запроса страницы. По крайней мере, это то, что я узнал;) Открытие и закрытие требует времени.
Дэвид Мортенссон
8
@David Martensson - соединения фактически не открываются и не закрываются при вызове SqlConnection.Open. ASP.NET перерабатывает активные подключения из пула, когда строка подключения совпадает с ранее использованной строкой подключения. Накладные расходы, связанные с этим, несущественны, и, кроме того, попытка «сделать это самостоятельно» означает, что вы должны взять на себя все задачи управления по обеспечению активности соединения для каждого последующего использования, что добавляет сложности и накладных расходов. При использовании пула соединений рекомендуется открывать и закрывать его при каждом использовании.
Джейми Треворджи,
2
При всем моем уважении, ответ «Всегда закрывать связи» не очень подходит к вопросу ... Я их закрываю. Вопрос в том, когда.
Alex
@David Martensson "Один раз для каждой страницы" упрощено. Вы правы в том, что если у вас есть несколько команд базы данных, которые нужно выполнить одну за другой, вы можете оставить соединение открытым, пока выполняете их. Если вы закроете и снова откроете, возникнут незначительные накладные расходы - соединение войдет в пул и будет извлечено из него через мгновение.
Concrete Gannet
1
@ Дэвид Мартенсон Но никогда не оставляйте соединение без дела. Если вы ждете действия пользователя или чего-то еще, закройте его. Если сомневаетесь, закройте. Вы открываете так поздно, как только можете, в надежде, что кто-то еще завершил соединение и объединил его. Затем вы отвечаете за услугу - закрывайте как можно раньше.
Concrete Gannet
13

Отказ от ответственности: я знаю, что это устарело, но я нашел простой способ продемонстрировать этот факт, поэтому вкладываю свои два цента.

Если вам сложно поверить в то, что объединение в пул действительно будет быстрее, попробуйте следующее:

Добавьте где-нибудь следующее:

using System.Diagnostics;
public static class TestExtensions
{
    public static void TimedOpen(this SqlConnection conn)
    {
        Stopwatch sw = Stopwatch.StartNew();
        conn.Open();
        Console.WriteLine(sw.Elapsed);
    }
}

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

Если вы хотите пометить их, вы можете добавить их new StackTrace(true).GetFrame(1) +к вызову WriteLine.

Крис Пфол
источник
9

Есть различия между физическими и логическими соединениями. DbConnection - это своего рода логическое соединение, использующее базовое физическое соединение с Oracle. Закрытие / открытие DbConnection не влияет на вашу производительность, но делает ваш код чистым и стабильным - утечки соединения в этом случае невозможны.

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

Пул соединений освобождает вас от проверки состояния соединения - просто откройте, используйте и сразу же закройте.

Тони Х
источник
Да, соединение не является соединением, т.е. DbConnection не является физическим соединением. DbConnection - это класс .NET, который предоставляет методы и свойства для управления базовым физическим соединением.
Concrete Gannet
К сожалению, не сразу было очевидно, что все это было сделано неявно, но документация подробно описывает это. docs.microsoft.com/en-us/dotnet/framework/data/adonet/…
Остин Салгат,
2

Обычно вы должны сохранять одно соединение для каждой транзакции (без параллельных вычислений)

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

Даже если у ado.net есть свой пул соединений, стоимость диспетчерского соединения очень низкая, но соединение с повторным использованием - более лучший выбор.

Почему бы не сохранить только одно соединение в приложении

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

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

ShiningRush
источник