"Open / Close" SqlConnection oder offen halten?

121

Ich habe meine Geschäftslogik in einfachen statischen Klassen mit statischen Methoden implementiert. Jede dieser Methoden öffnet / schließt die SQL-Verbindung, wenn sie aufgerufen wird:

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

        // ...

        connection.Close();
    }
}

Ich denke jedoch, dass das Vermeiden des Öffnens und Schließens einer Verbindung die Leistung spart . Ich habe vor einiger Zeit einige Tests mit der OleDbConnection- Klasse durchgeführt ( ich bin mir bei SqlConnection nicht sicher), und es hat definitiv geholfen, so zu funktionieren (soweit ich mich erinnere):

//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();
    }
}

Die Frage ist also: Soll ich die Methode (a) oder die Methode (b) wählen? Ich habe in einer anderen Frage zum Stackoverflow gelesen, dass das Verbindungspooling die Leistung für mich gespart hat. Ich muss mich überhaupt nicht darum kümmern ...

PS. Es ist eine ASP.NET-App - Verbindungen bestehen nur während einer Webanforderung. Keine Win-App oder Service.

Alex
quelle
1
Nur ein Hinweis: Verwenden Sie das DbConnection.StateChangeEreignis, um Änderungen der Statusänderung der Verbindung zu überwachen (und möglicherweise lokal zu speichern), anstatt die DbConnection.StateEigenschaft direkt zu überprüfen . Sie sparen Leistungskosten.
Decyclone
1
Ein Detail, das fehlt, ist, wie diese Methode Teil einer Seitenanforderung ist. Ist es die einzige Methode, die aufgerufen wird, oder ist es, wie ich in meiner Antwort angenommen habe, eine von vielen Methoden, die in einer Seitenanfrage aufgerufen werden? Es beeinflusst, welche Antwort richtig ist;)
David Mårtensson
David - Viele Methoden wie diese werden aufgerufen :)
Alex
1
Fall A zeigt einen Mangel an Vertrauen in Dispose: siehe stackoverflow.com/questions/1195829/… und das Beispiel auf MSDN msdn.microsoft.com/en-us/library/…
user2864740

Antworten:

82

Halten Sie sich an Option a .

Das Verbindungspooling ist dein Freund.

Adriaan Stander
quelle
37
IMHO - er sollte nicht einmal schließen. Die Entsorgung wird es tun.
Royi Namir
2
@ RoyiNamir Ich mag den Anruf, um die Verbindung zu schließen. Besonders für Anfänger und Neulinge in einer Codebasis. Es ist expliziter und lesbarer.
Kanten
27
@edhedges Die Verwendung von "using" und Close () wird letztendlich nur bei Neulingen Verwirrung stiften. Sie werden den Zweck der Verwendung von "using" nicht verstehen. Verwenden Sie nicht "Schließen", sondern bringen Sie ihnen den Zweck des "Verwendens" bei. Damit sie lernen können, besser zu werden und das Gelernte auf andere Teile des Codes anzuwenden.
Luis Perez
1
Sollte / muss das "Open ()" aufgerufen werden? Ich benutze es derzeit so: using (var conn = GetConnection ()) {} public SqlConnection GetConnection () {return new SqlConnection (_connectionString); }
Ganders
79

Verwenden Sie jedes Mal Methode (a). Wenn Sie mit der Skalierung Ihrer Anwendung beginnen, wird die Logik, die sich mit dem Status befasst, zu einem echten Problem, wenn Sie dies nicht tun.

Connection Pooling macht das, was es verspricht. Denken Sie nur daran, was passiert, wenn die Anwendung skaliert wird und wie schwierig es wäre, den Öffnungs- / Schließstatus der Verbindung manuell zu verwalten. Der Verbindungspool erledigt dies sehr gut automatisch. Wenn Sie sich Sorgen um die Leistung machen, denken Sie an einen Speicher-Cache-Mechanismus, damit nichts blockiert wird.

WeNeedAnswers
quelle
33

Schließen Sie Verbindungen immer, sobald Sie mit ihnen fertig sind, damit die zugrunde liegende Datenbankverbindung wieder in den Pool aufgenommen und für andere Anrufer verfügbar sein kann. Das Verbindungspooling ist ziemlich gut optimiert, daher gibt es dafür keine nennenswerten Nachteile. Der Rat ist im Grunde der gleiche wie bei Transaktionen - halten Sie sie kurz und knapp, wenn Sie fertig sind.

Es wird komplizierter, wenn Sie auf MSDTC-Probleme stoßen, indem Sie eine einzelne Transaktion für Code verwenden, der mehrere Verbindungen verwendet. In diesem Fall müssen Sie das Verbindungsobjekt tatsächlich freigeben und erst schließen, wenn die Transaktion abgeschlossen ist.

Da Sie die Dinge hier jedoch von Hand erledigen, sollten Sie Tools untersuchen, die Verbindungen für Sie verwalten, z. B. DataSets, Linq to SQL, Entity Framework oder NHibernate.

Neil Barnwell
quelle
Normalerweise sollten Sie eine Verbindung nicht bei jedem Methodenaufruf nur einmal für jede Seitenanforderung öffnen und schließen. Das habe ich zumindest gelernt;) Öffnen und Schließen kostet Zeit.
David Mårtensson
8
@ David Martensson - Verbindungen werden beim Aufrufen von SqlConnection.Open nicht geöffnet und geschlossen. ASP.NET recycelt aktive Verbindungen aus dem Pool, wenn die Verbindungszeichenfolge mit einer zuvor verwendeten Verbindungszeichenfolge übereinstimmt. Der damit verbundene Overhead spielt keine Rolle. Wenn Sie versuchen, es selbst zu tun, müssen Sie außerdem alle Verwaltungsaufgaben übernehmen, um sicherzustellen, dass die Verbindung für jede nachfolgende Verwendung noch aktiv ist. Dies erhöht die Komplexität und den Overhead. Beim Verbindungspooling empfiehlt es sich, es für jede Verwendung zu öffnen und zu schließen.
Jamie Treworgy
2
Bei allem Respekt passt die Antwort "Immer enge Verbindungen" nicht sehr gut zu der Frage ... Ich schließe sie. Die Frage ist - wann.
Alex
@ David Martensson "Einmal für jede Seite" ist zu stark vereinfacht. Sie haben Recht, wenn Sie mehrere Datenbankbefehle nacheinander ausführen müssen, können Sie die Verbindung offen halten, während Sie sie ausführen. Wenn Sie schließen und wieder öffnen, entsteht ein geringer Overhead. Die Verbindung wird in den Pool verschoben und einen Moment später wieder abgerufen.
Concrete Gannet
1
@ David Martensson Aber halten Sie niemals eine inaktive Verbindung. Wenn Sie auf eine Aktion des Benutzers oder auf etwas anderes warten, schließen Sie sie. Wenn Sie Zweifel haben, schließen Sie es. Sie öffnen so spät wie möglich in der Hoffnung, dass jemand anderes eine Verbindung beendet und diese zusammengefasst hat. Dann erwidern Sie den Gefallen - schließen Sie so früh wie möglich.
Concrete Gannet
13

Haftungsausschluss: Ich weiß, dass dies alt ist, aber ich habe einen einfachen Weg gefunden, diese Tatsache zu demonstrieren, also setze ich meine zwei Cent ein.

Wenn Sie Probleme haben zu glauben, dass das Pooling wirklich schneller sein wird, probieren Sie Folgendes aus:

Fügen Sie irgendwo Folgendes hinzu:

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

Ersetzen Sie nun alle Aufrufe von Open()durch TimedOpen()und führen Sie Ihr Programm aus. Für jede einzelne Verbindungszeichenfolge, die Sie haben, wird im Konsolenfenster (Ausgabefenster) ein einziges langes Öffnen geöffnet und eine Reihe sehr schneller Fenster geöffnet.

Wenn Sie sie kennzeichnen möchten, können Sie new StackTrace(true).GetFrame(1) +sie dem Anruf hinzufügen WriteLine.

Chris Pfohl
quelle
9

Es gibt Unterschiede zwischen physischen und logischen Verbindungen. DbConnection ist eine Art logische Verbindung und verwendet die zugrunde liegende physische Verbindung zu Oracle. Das Schließen / Öffnen von DbConnection wirkt sich nicht auf Ihre Leistung aus, macht Ihren Code jedoch sauber und stabil - Verbindungslecks sind in diesem Fall nicht möglich.

Sie sollten sich auch an Fälle erinnern, in denen es Einschränkungen für parallele Verbindungen auf dem Datenbankserver gibt. Berücksichtigen Sie dies, dass Ihre Verbindungen sehr kurz sein müssen.

Der Verbindungspool befreit Sie von der Überprüfung des Verbindungsstatus - öffnen, verwenden und schließen Sie sie einfach sofort.

Tony Kh
quelle
Ja, die Verbindung ist nicht die Verbindung - dh DbConnection ist nicht die physische Verbindung. DbConnection ist eine .NET-Klasse, die Methoden und Eigenschaften zum Bearbeiten der zugrunde liegenden physischen Verbindung bereitstellt.
Concrete Gannet
Leider war es nicht sofort offensichtlich, dass dies alles implizit gemacht wurde, aber die Dokumentation geht darauf ein. docs.microsoft.com/en-us/dotnet/framework/data/adonet/…
Austin Salgat
2

Normalerweise sollten Sie für jede Transaktion eine Verbindung beibehalten (keine parallelen Berechnungen).

Wenn der Benutzer beispielsweise eine Ladeaktion ausführt, muss Ihre Anwendung zuerst den Kontostand des Benutzers ermitteln und aktualisieren. Er sollte dieselbe Verbindung verwenden.

Selbst wenn ado.net über einen Verbindungspool verfügt, sind die Kosten für den Versand von Verbindungen sehr gering, aber die Wiederverwendung von Verbindungen ist die bessere Wahl.

Warum nicht nur eine Verbindung in der Anwendung behalten

Da die Verbindung blockiert, wenn Sie eine Abfrage oder einen Befehl ausführen, bedeutet dies, dass Ihre Anwendung gleichzeitig nur eine Datenbankoperation ausführt, wie schlecht die Leistung ist.

Ein weiteres Problem ist, dass Ihre Anwendung immer eine Verbindung hat, obwohl Ihr Benutzer sie nur geöffnet hat, aber keine Vorgänge. Wenn viele Benutzer Ihre Anwendung öffnen, kostet der Datenbankserver in Kürze die gesamte Verbindungsquelle, während Ihre Benutzer dies nicht getan haben etwas.

ShiningRush
quelle