Sorry dat ik in de eerste plaats alleen commentaar geef, maar ik plaats bijna elke dag een soortgelijke opmerking, omdat veel mensen denken dat het slim zou zijn om ADO.NET-functionaliteit in een DB-Klasse in te kapselen (ik ook 10 jaar geleden). Meestal besluiten ze om statische/gedeelde objecten te gebruiken, omdat dit sneller lijkt te zijn dan het maken van een nieuw object voor welke actie dan ook.
Dat is noch in termen van prestaties, noch in termen van faalveiligheid een goed idee.
Stroperij niet op het grondgebied van de Connection-Pool
Er is een goede reden waarom ADO.NET intern de onderliggende verbindingen met het DBMS in de ADO-NET Connection-Pool beheert:
In de praktijk gebruiken de meeste toepassingen slechts één of enkele verschillende configuraties voor verbindingen. Dit betekent dat tijdens het uitvoeren van applicaties veel identieke verbindingen herhaaldelijk worden geopend en gesloten. Om de kosten van het openen van verbindingen te minimaliseren, gebruikt ADO.NET een optimalisatietechniek die verbindingspooling wordt genoemd.
Pooling van verbindingen vermindert het aantal keren dat nieuwe verbindingen moeten worden geopend. De pooler blijft eigenaar van de fysieke verbinding. Het beheert verbindingen door een reeks actieve verbindingen voor elke gegeven verbindingsconfiguratie in leven te houden. Telkens wanneer een gebruiker Open aanroept op een verbinding, zoekt de pooler naar een beschikbare verbinding in de pool. Als er een gepoolde verbinding beschikbaar is, wordt deze teruggestuurd naar de beller in plaats van een nieuwe verbinding te openen. Wanneer de toepassing Sluiten aanroept voor de verbinding, stuurt de pooler deze terug naar de gepoolde set actieve verbindingen in plaats van deze te sluiten. Zodra de verbinding is hersteld naar de pool, is deze klaar om opnieuw te worden gebruikt bij de volgende Open oproep.
Er is dus duidelijk geen reden om het creëren, openen of sluiten van verbindingen te vermijden, aangezien ze eigenlijk helemaal niet worden gemaakt, geopend en gesloten. Dit is "slechts" een vlag voor de verbindingspool om te weten wanneer een verbinding opnieuw kan worden gebruikt of niet. Maar het is een zeer belangrijke vlag, want als een verbinding "in gebruik" is (de verbindingspool gaat ervan uit), moet er een nieuwe fysieke verbinding worden geopend met het DBMS, wat erg duur is.
U behaalt dus geen prestatieverbetering, maar het tegenovergestelde. Als de maximale opgegeven poolgrootte (100 is de standaard) wordt bereikt, krijgt u zelfs uitzonderingen (te veel open verbindingen ...). Dit zal dus niet alleen de prestaties enorm beïnvloeden, maar ook een bron zijn voor vervelende fouten en (zonder Transacties te gebruiken) een data-dumping-gebied.
Als je zelfs statische verbindingen gebruikt, creëer je een slot voor elke thread die toegang probeert te krijgen tot dit object. ASP.NET is van nature een multithreading-omgeving. Er is dus een grote kans voor deze sloten die op zijn best prestatieproblemen veroorzaken. Eigenlijk krijg je vroeg of laat veel verschillende uitzonderingen (zoals je ExecuteReader vereist een open en beschikbare verbinding ).
Conclusie :
- Hergebruik helemaal geen verbindingen of ADO.NET-objecten.
- Maak ze niet statisch/gedeeld (in VB.NET)
- Maak, open (in het geval van verbindingen), gebruik, sluit en verwijder ze waar je ze nodig hebt (bijvoorbeeld in een methode)
- gebruik de
using-statement
impliciet verwijderen en sluiten (in het geval van Verbindingen)
Dat geldt niet alleen voor Connections (hoewel het meest opvallend). Elk object dat IDisposable
implementing implementeert moet worden verwijderd (eenvoudigst door using-statement
), des te meer in de System.Data.SqlClient
naamruimte.
Al het bovenstaande spreekt tegen een aangepaste DB-klasse die alle objecten inkapselt en opnieuw gebruikt. Dat is de reden waarom ik commentaar gaf om het weg te gooien. Dat is alleen een probleembron.
Bewerken :Hier is een mogelijke implementatie van uw 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;
}