Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

ExecuteReader erfordert eine offene und verfügbare Verbindung. Der aktuelle Status der Verbindung ist Verbinden

Tut mir leid, dass ich überhaupt erst kommentiere, aber ich poste fast jeden Tag einen ähnlichen Kommentar, da viele Leute denken, dass es klug wäre, die ADO.NET-Funktionalität in eine DB-Klasse zu kapseln (ich auch vor 10 Jahren). Meistens entscheiden sie sich für die Verwendung statischer/gemeinsamer Objekte, da dies schneller zu sein scheint, als für eine Aktion ein neues Objekt zu erstellen.

Das ist weder hinsichtlich der Leistung noch hinsichtlich der Ausfallsicherheit eine gute Idee.

Wildle nicht auf dem Territorium des Connection-Pools

Es gibt einen guten Grund, warum ADO.NET die zugrunde liegenden Verbindungen zum DBMS intern im ADO-NET Connection-Pool verwaltet:

In der Praxis verwenden die meisten Anwendungen nur eine oder wenige unterschiedliche Konfigurationen für Verbindungen. Dies bedeutet, dass während der Anwendungsausführung viele identische Verbindungen wiederholt geöffnet und geschlossen werden. Um die Kosten für das Öffnen von Verbindungen zu minimieren, verwendet ADO.NET eine Optimierungstechnik namens Connection Pooling.

Das Verbindungspooling reduziert die Anzahl der Male, die neue Verbindungen geöffnet werden müssen. Der Pooler behält das Eigentum an der physikalischen Verbindung. Es verwaltet Verbindungen, indem es einen Satz aktiver Verbindungen für jede gegebene Verbindungskonfiguration aufrechterhält. Immer wenn ein Benutzer Open auf einer Verbindung aufruft, sucht der Pooler nach einer verfügbaren Verbindung im Pool. Wenn eine gepoolte Verbindung verfügbar ist, wird sie an den Anrufer zurückgegeben, anstatt eine neue Verbindung zu öffnen. Wenn die Anwendung Close für die Verbindung aufruft, gibt der Pooler sie an den gepoolten Satz aktiver Verbindungen zurück, anstatt sie zu schließen. Sobald die Verbindung an den Pool zurückgegeben wurde, kann sie beim nächsten Open-Aufruf wiederverwendet werden.

Es gibt also offensichtlich keinen Grund, das Erstellen, Öffnen oder Schließen von Verbindungen zu vermeiden, da sie tatsächlich überhaupt nicht erstellt, geöffnet und geschlossen werden. Dies ist "nur" ein Flag für den Verbindungspool, um zu wissen, wann eine Verbindung wiederverwendet werden kann oder nicht. Aber es ist ein sehr wichtiges Flag, denn wenn eine Verbindung "in Gebrauch" ist (der Verbindungspool geht davon aus), muss eine neue physische Verbindung zum DBMS geöffnet werden, was sehr teuer ist.

Sie erzielen also keine Leistungssteigerung, sondern das Gegenteil. Wenn die angegebene maximale Poolgröße (100 ist der Standardwert) erreicht ist, würden Sie sogar Ausnahmen erhalten (zu viele offene Verbindungen ...). Dies wird also nicht nur die Leistung enorm beeinträchtigen, sondern auch eine Quelle für böse Fehler und (ohne die Verwendung von Transaktionen) ein Daten-Dumping-Bereich sein.

Wenn Sie sogar statische Verbindungen verwenden, erstellen Sie eine Sperre für jeden Thread, der versucht, auf dieses Objekt zuzugreifen. ASP.NET ist von Natur aus eine Multithreading-Umgebung. Es besteht also eine große Chance für diese Sperren, die bestenfalls zu Leistungsproblemen führen. Tatsächlich werden Sie früher oder später viele verschiedene Ausnahmen erhalten (wie Ihr ExecuteReader eine offene und verfügbare Verbindung benötigt ).

Fazit :

  • Verwenden Sie Verbindungen oder ADO.NET-Objekte überhaupt nicht wieder.
  • Machen Sie sie nicht statisch/geteilt (in VB.NET)
  • Erstellen, öffnen (im Falle von Verbindungen), verwenden, schließen und entsorgen Sie sie immer dort, wo Sie sie brauchen (z. B. in einer Methode)
  • Verwenden Sie die using-statement zu entsorgen und zu schließen (im Falle von Verbindungen) implizit

Das gilt nicht nur für Connections (wenn auch am auffälligsten). Jedes Objekt, das IDisposable implementiert sollte entsorgt werden (am einfachsten per using-statement ), erst recht im System.Data.SqlClient Namensraum.

All dies spricht gegen eine benutzerdefinierte DB-Klasse, die alle Objekte kapselt und wiederverwendet. Das ist der Grund, warum ich kommentiert habe, es zu entsorgen. Das ist nur eine Problemquelle.

Bearbeiten :Hier ist eine mögliche Implementierung Ihres retrievePromotion -Methode:

public Promotion retrievePromotion(int promotionID)
{
    Promotion promo = null;
    var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["MainConnStr"].ConnectionString;
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        var queryString = "SELECT PromotionID, PromotionTitle, PromotionURL FROM Promotion WHERE [email protected]";
        using (var da = new SqlDataAdapter(queryString, connection))
        {
            // you could also use a SqlDataReader instead
            // note that a DataTable does not need to be disposed since it does not implement IDisposable
            var tblPromotion = new DataTable();
            // avoid SQL-Injection
            da.SelectCommand.Parameters.Add("@PromotionID", SqlDbType.Int);
            da.SelectCommand.Parameters["@PromotionID"].Value = promotionID;
            try
            {
                connection.Open(); // not necessarily needed in this case because DataAdapter.Fill does it otherwise 
                da.Fill(tblPromotion);
                if (tblPromotion.Rows.Count != 0)
                {
                    var promoRow = tblPromotion.Rows[0];
                    promo = new Promotion()
                    {
                        promotionID    = promotionID,
                        promotionTitle = promoRow.Field<String>("PromotionTitle"),
                        promotionUrl   = promoRow.Field<String>("PromotionURL")
                    };
                }
            }
            catch (Exception ex)
            {
                // log this exception or throw it up the StackTrace
                // we do not need a finally-block to close the connection since it will be closed implicitely in an using-statement
                throw;
            }
        }
    }
    return promo;
}