Désolé de ne commenter qu'à la base, mais je poste presque tous les jours un commentaire similaire depuis que de nombreuses personnes pensent qu'il serait intelligent d'encapsuler la fonctionnalité ADO.NET dans une classe DB (moi aussi, il y a 10 ans). La plupart du temps, ils décident d'utiliser des objets statiques/partagés car il semble que ce soit plus rapide que de créer un nouvel objet pour chaque action.
Ce n'est pas une bonne idée en termes de performances ni en termes de fiabilité.
Ne pas empiéter sur le territoire du pool de connexions
Il y a une bonne raison pour laquelle ADO.NET gère internalement les connexions sous-jacentes au SGBD dans le Pool de connexions ADO-NET:
En pratique, la plupart des applications n'utilisent qu'une ou quelques configurations différentes de connexions. Cela signifie que pendant l'exécution de l'application, de nombreuses connexions identiques seront ouvertes et fermées à plusieurs reprises. Pour minimiser le coût de l'ouverture des connexions, ADO.NET utilise une technique d'optimisation appelée pool de connexions.
Le pool de connexions réduit le nombre de fois que de nouvelles connexions doivent être ouvertes. Le gestionnaire de pool conserve la propriété de la connexion physique. Il gère les connexions en maintenant un ensemble de connexions actives pour chaque configuration de connexion donnée. Chaque fois qu'un utilisateur appelle Open sur une connexion, le gestionnaire de pool recherche une connexion disponible dans le pool. Si une connexion mise en pool est disponible, il la renvoie à l'appelant au lieu d'ouvrir une nouvelle connexion. Lorsque l'application appelle Close sur la connexion, le gestionnaire de pool la renvoie à l'ensemble mis en pool des connexions actives au lieu de la fermer. Une fois que la connexion est retournée au pool, elle est prête à être réutilisée lors du prochain appel Open.
Il n'y a donc aucune raison d'éviter de créer, d'ouvrir ou de fermer des connexions puisqu'en réalité, elles ne sont ni créées, ni ouvertes, ni fermées du tout. C'est "seulement" un indicateur pour le pool de connexions de savoir quand une connexion peut être réutilisée ou non. Mais c'est un indicateur très important, car si une connexion est "en cours d'utilisation" (supposée par le pool de connexions), une nouvelle connexion physique doit être ouverte vers le SGBD, ce qui est très coûteux.
Vous n'obtenez donc aucune amélioration des performances, bien au contraire. Si la taille maximale du pool spécifiée (100 est la valeur par défaut) est atteinte, vous pourriez même obtenir des exceptions (trop de connexions ouvertes ...). Cela n'impactera pas seulement les performances de manière importante, mais sera aussi une source d'erreurs désagréables et (sans utiliser de transactions) d'une zone de vidage des données.
Si vous utilisez même des connexions statiques, vous créez un verrou pour chaque thread essayant d'accéder à cet objet. ASP.NET est un environnement multi-threading par nature. Il y a donc de fortes chances pour que ces verrous causent des problèmes de performances au mieux. En fait, tôt ou tard, vous obtiendrez de nombreuses exceptions différentes (comme votre ExecuteReader nécessite une connexion ouverte et disponible).
Conclusion:
- Ne réutilisez pas les connexions ni aucun objet ADO.NET du tout.
- Ne les rendez pas statiques/partagés (en VB.NET)
- Créez toujours, ouvrez (dans le cas des connexions), utilisez, fermez et disposez-les là où vous en avez besoin (par exemple, dans une méthode).
- Utilisez la
instruction using
pour disposer et fermer (dans le cas des connexions) de manière implicite
Ceci est valable non seulement pour les connexions (bien que cela soit le plus remarquable). Chaque objet implémentant IDisposable
doit être disposé (le plus simplement par une instruction using
), d'autant plus dans l'espace de noms System.Data.SqlClient
.
Tout ce qui précède plaide contre une classe DB personnalisée qui encapsule et réutilise tous les objets. C'est la raison pour laquelle j'ai commenté pour la jeter à la poubelle. C'est seulement une source de problèmes.
Éditer : Voici une implémentation possible de votre méthode retrievePromotion
:
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))
{
// vous pourriez également utiliser un SqlDataReader à la place
// notez qu'un DataTable n'a pas besoin d'être disposé car il n'implémente pas IDisposable
var tblPromotion = new DataTable();
// évitez les injections SQL
da.SelectCommand.Parameters.Add("@PromotionID", SqlDbType.Int);
da.SelectCommand.Parameters["@PromotionID"].Value = promotionID;
try
{
connection.Open(); // pas nécessairement nécessaire dans ce cas car DataAdapter.Fill le fait autrement
da.Fill(tblPromotion);
if (tblPromotion.Rows.Count != 0)
{
var promoRow = tblPromotion.Rows[0];
promo = new Promotion()
{
promotionID = promotionID,
promotionTitle = promoRow.Field("PromotionTitle"),
promotionUrl = promoRow.Field("PromotionURL")
};
}
}
catch (Exception ex)
{
// journalisez cette exception ou remontez-la dans la pile d'appels
// nous n'avons pas besoin d'un bloc finally pour fermer la connexion car elle sera fermée implicitement dans une instruction using
throw;
}
}
}
return promo;
}
25 votes
Ne pas utiliser de connexions partagées/statiques dans un environnement multithread comme ASP.NET car cela générera des verrous ou des exceptions (trop de connexions ouvertes, etc.). Jetez votre classe DB à la poubelle et créez, ouvrez, utilisez, fermez, supprimez des objets ado.net où vous en avez besoin. Jetez un œil également à l'instruction using.
2 votes
Pouvez-vous me donner des détails sur les fonctions SqlOpenConnection (); et sql.ExecuteReader ();?..
0 votes
Private void SqlOpenConnection() { try { conn = new SqlConnection(); conn.ConnectionString = conString; conn.Open(); } catch (SqlException ex) { throw ex; } }
0 votes
@GuoHongLim : j'ai oublié de mentionner que même une
conString
statique n'ajoute rien en termes de performance puisqu'elle est mise en cache par défaut de toute façon (comme toutes les valeurs de configuration pour l'application actuelle).0 votes
...et juste pour en faire une chose connue-inconnue : Assurer que vous avez également correctement géré la transaction de votre base de données / unité de travail est laissé comme exercice pour le lecteur.