Es tut mir leid, dass ich erst einmal kommentiert habe, 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 statische / gemeinsam genutzte Objekte, da dies schneller zu sein scheint als das Erstellen eines neuen Objekts für eine Aktion.
Das ist weder in Bezug auf die Leistung noch in Bezug auf die Ausfallsicherheit eine gute Idee.
Wildern Sie nicht auf dem Territorium des Verbindungspools
Es gibt einen guten Grund, warum ADO.NET die zugrunde liegenden Verbindungen zum DBMS im ADO-NET-Verbindungspool intern verwaltet :
In der Praxis verwenden die meisten Anwendungen nur eine oder mehrere 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, die als Verbindungspooling bezeichnet wird.
Verbindungspooling reduziert die Häufigkeit, mit der neue Verbindungen geöffnet werden müssen. Der Pooler behält das Eigentum an der physischen Verbindung. Es verwaltet Verbindungen, indem es eine Reihe aktiver Verbindungen für jede gegebene Verbindungskonfiguration am Leben erhält. Immer wenn ein Benutzer Open für eine Verbindung aufruft, sucht der Pooler nach einer verfügbaren Verbindung im Pool. Wenn eine gepoolte Verbindung verfügbar ist, gibt sie diese an den Anrufer zurück, anstatt eine neue Verbindung zu öffnen. Wenn die Anwendung Close für die Verbindung aufruft, gibt der Pooler sie an die gepoolten aktiven Verbindungen zurück, anstatt sie zu schließen. Sobald die Verbindung zum Pool wiederhergestellt ist, kann sie beim nächsten offenen Anruf wiederverwendet werden.
Es gibt also offensichtlich keinen Grund, das Erstellen, Öffnen oder Schließen von Verbindungen zu vermeiden, da diese überhaupt nicht erstellt, geöffnet und geschlossen werden. Dies ist "nur" ein Flag, mit dem der Verbindungspool weiß, 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 Leistungsverbesserung, sondern das Gegenteil. Wenn die angegebene maximale Poolgröße (100 ist die Standardeinstellung) erreicht ist, erhalten Sie sogar Ausnahmen (zu viele offene Verbindungen ...). Dies wirkt sich also nicht nur enorm auf die Leistung aus, sondern ist auch eine Quelle für böse Fehler und (ohne Verwendung von Transaktionen) ein Daten-Dumping-Bereich.
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 gibt also eine große Chance für diese Sperren, was bestenfalls zu Leistungsproblemen führt. Früher oder später erhalten Sie viele verschiedene Ausnahmen (wie Ihr ExecuteReader eine offene und verfügbare Verbindung benötigt ).
Fazit :
- Verwenden Sie keine Verbindungen oder ADO.NET-Objekte.
- Machen Sie sie nicht statisch / freigegeben (in VB.NET)
- Erstellen, öffnen (bei Verbindungen), verwenden, schließen und entsorgen Sie sie immer dort, wo Sie sie benötigen (z. B. in einer Methode).
- Verwenden Sie das
using-statement
, um (bei Verbindungen) implizit zu entsorgen und zu schließen
Dies gilt nicht nur für Verbindungen (obwohl dies am auffälligsten ist). Jedes Objekt, das implementiert wird, IDisposable
sollte entsorgt werden (am einfachsten von using-statement
), umso mehr im System.Data.SqlClient
Namespace.
All dies spricht gegen eine benutzerdefinierte DB-Klasse, die alle Objekte kapselt und wiederverwendet. Das ist der Grund, warum ich kommentiert habe, es zu verwerfen. Das ist nur eine Problemquelle.
Bearbeiten : Hier ist eine mögliche Implementierung Ihrer 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 PromotionID=@PromotionID";
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;
}