2010-08-24 10 views
3

Je suis en train de concevoir un wrapper de base de données pour C#. Voici les deux options que j'ai:Conception de l'encapsuleur de base de données C#

Option A:

class DBWrapper:IDisposable 
{ 
    private SqlConnection sqlConn; 

    public DBWrapper() 
    { 
      sqlConn = new SqlConnection("my connection string"); 
      sqlConn.Open(); 
    } 

    public DataTable RunQuery(string Sql) 
    { 
       implementation...... 
    } 

    public Dispose() 
    { 
      if(sqlConn != null) 
        sqlConn.Close(); 
    } 
} 

Option B:

class DBWrapper 
{ 
    public DBWrapper() 
    {    
    } 

    public DataTable RunQuery(string Sql) 
    { 
      SqlConnection sqlConn = new SqlConnection("my connection string"); 
      .....implementation...... 
      sqlConn.Close();    
    } 
} 

Pour option lorsque la classe est instancié est ouvert une connexion. Ainsi, peu importe le nombre de fois que l'appelant appelle RunQuery, la connexion est toujours prête. Mais si l'application instancie DBWrapper au début de l'application, la connexion sera simplement ouverte et ne rien faire jusqu'à ce que l'application soit terminée. En outre, il peut y avoir plusieurs DBWrapper instanciés pendant l'exécution. Donc, c'est un gaspillage de ressources.

Pour l'option B, l'option A ne l'est pas, mais une nouvelle connexion doit être ouverte et fermée chaque fois que l'appelant appelle RunQuery. Je ne suis pas sûr à quel point cela nuira à la performance.

Veuillez partager votre expertise. Merci pour la lecture.

+1

Ils ont tendance à avoir une mauvaise réputation ici, mais ont une lecture sur le motif de conception Singleton, c'est la seule fois que j'utilise singletons – acqu13sce

+0

@ acqu13sce: Pourquoi la mauvaise réputation? Est-ce la raison de la fausse capacité? – kbrimington

+0

Juste quelques commentaires que j'ai lus en passant, y compris ce wiki de la communauté http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons Je pense personnellement qu'ils ont leur place mais peut-être faire – acqu13sce

Répondre

6

Pour des raisons de performance, vous aurez certainement envie de ne pas aller avec l'option B Laissez-moi suggérer l'option C (au moins dans les cas j'ai vécu.):

class DBWrapper:IDisposable { private SqlConnection sqlConn; 

public void EnsureConnectionIsOpen() 
{ 
    if (sqlConn == null) 
    { 
     sqlConn = new SqlConnection("my connection string"); 
     sqlConn.Open(); 
    } 

} 

public DataTable RunQuery(string Sql) 
{ 
    EnsureConnectionIsOpen(); 
    implementation...... 
} 

public Dispose() 
{ 
    if(sqlConn != null) 
       sqlConn.Close(); 
} 

} 

Vous pouvez envisager d'utiliser le modèle singleton pour vous assurer qu'il n'y a qu'une seule instance de votre DBWrapper.

+1

Notez que lorsque vous utilisez le modèle singleton, vous pouvez rencontrer des problèmes de multithreading (si le modèle est incorrect). Bien que les singletons soient souvent de bonnes solutions pour ce type de code, une personne doit toujours être vigilante et vérifier les problèmes de multithreading lors de l'utilisation de la statique dans une application. – Gertjan

+1

Singleton n'est qu'un moyen de partager des objets entre les threads, mais il est évident que vous devez toujours concevoir votre multithreading avec soin dès que vous partagez des objets entre threads, peu importe comment ils sont partagés. – Philipp

+0

Si vous partagez une connexion de base de données entre thread (et dans un scénario Web entre les utilisateurs), vous risquez de rencontrer des situations "amusantes". Cela vous empêchera également d'avoir plusieurs connexions. Avoir plusieurs connexions est parfois nécessaire lors de la collecte de données dans les relations parents-> enfants, dans ce cas, vous ouvrez une deuxième connexion pour obtenir les enfants pendant que la première connexion est utilisée pour rassembler les parents. – Gertjan

2

Quelques commentaires: utile d'envisager

Dans l'approche où vous gérez un (peut-être) la connexion à long terme, il est important de vérifier si la connexion est ouverte avant d'exécuter une requête. J'ai rencontré des problèmes avant où NETCF a fermé les connexions inutilisées après un certain temps. Dans l'approche où vous ouvrez une nouvelle connexion par requête, assurez-vous que votre connexion, vos commandes et (le cas échéant) les lecteurs de données sont correctement enveloppés dans des instructions ou essayez/enfin + disposez() des blocs à libérer connexions et serrures.

Bonne codification!

+0

Merci pour votre rappeler. Pour l'option A, si je m'assure que ma classe n'expose aucune fonction pour fermer la connexion et qu'il y a toujours un constructeur pour ouvrir la connexion, cela devrait être OK, n'est-ce pas? Ya, j'utilise l'instruction "using". – Dreteh

+0

@dreteh: Je recommande de fournir une propriété privée/protégée pour vérifier la connexion avant utilisation. Quelque chose comme 'protégé SqlConnection Connection {get {if (sqlConn.State == ConnectionState.Closed) sqlConn.Open(); renvoie sqlConn; }} '. En tant que déni, je n'ai jamais rencontré de problèmes avec le cadre fermant ma connexion en dehors du cadre compact. Peut-être que cette suggestion est exagérée pour une application complète. Je serais curieux de voir si la connexion se ferme automatiquement sur un ordinateur portable qui passe en mode veille ou hibernation. – kbrimington

1

L'option B est plus transactionnelle, ce qui a ses avantages. ADO.NET utilise le regroupement de connexions implicite, vous n'avez donc pas à vous soucier de la création fréquente de nouvelles instances de SqlConnection.

Vous devez déterminer si vous utilisez un modèle de données connecté ou déconnecté; comme la deuxième approche se prête mieux à un modèle déconnecté.

Mais comme je l'ai dit plus haut, le regroupement de connexions signifie qu'il n'y a pratiquement aucune différence sur le plan pratique.

+0

Votre réponse est très utile. Je considère l'option B parce que je veux surcharger la fonction RunQeury qui prend un objet de connexion supplémentaire afin que je puisse regrouper quelques accesseurs de données pour implémenter la transaction (de cette façon ils peuvent partager la même connexion). La seule chose qui me préoccupe vraiment est de continuer à ouvrir et fermer une connexion qui pourrait ne pas être efficace. – Dreteh

0

Vous pouvez avoir une option C où la base de données est ouverte sur demande dans RunQuery (si elle n'est pas ouverte) et fermée sur dispos (quand elle a été ouverte). De cette façon, la base de données n'est ouverte qu'en cas de besoin et ne sera ouverte qu'une seule fois.

donc dans le code pseudo:

class DBWrapper 
{ 

    public DBWrapper() 
    {    
    } 

    SqlConnection sqlConn = null; 

    public DataTable RunQuery(string Sql) 
    { 
      if(sqlConn == null) sqlConn = new SqlConnection("my connection string"); 
      .....implementation......    
    } 
    public Dispose() 
    { 
      if(sqlConn != null) 
        sqlConn.Close(); 
    } 

} 

esprit également que le moment Dispose est appelé est pas toujours directement après que l'objet n'est plus nécessaire (par exemple une variable de fonction après la fonction est utilisée).Autant que je sache, il sera exécuté quand le garbage collector recueille l'objet (ce qui n'est pas directement). Mais je ne suis pas totalement sûr de ça. Ce comportement peut également différer entre l'application Web et non Web.

+0

Mais je peux voir que cette conception aura aussi le problème de l'option A car imaginez si RunQuery est appelée au tout début de l'exécution et ne sera plus utilisée jusqu'à la fin. – Dreteh

+0

Cela est vrai mais la connexion sera ouverte en premier lieu alors que l'exemple A s'ouvre lors de la construction de l'objet. Si vous voulez resserrer la durée de vie de votre base de données, vous devez la fermer manuellement car le système ne peut pas deviner si et quand vous pourriez avoir besoin de la base de données dans le futur. – Gertjan

2

Le garbage collector est déclenché dans des conditions plutôt complexes mais il est invoqué lorsque la mémoire dépasse une certaine limite, il est également appelé périodiquement mais la période n'est pas constante. Vous ne pouvez jamais être sûr quand exactement le garbage collector dispose et par conséquent (dans une autre course) détruit l'objet. Une chose que vous pouvez être sûr est le fait que garbage collector ne disposera et ne détruira jamais l'objet qui a encore des références. Par exemple, un objet référencé via des variables statiques sur la classe ne sera ni détruit ni détruit.