2010-04-03 5 views
5

.J'ai deux questions en utilisant la question de la déclaration

1) Si vous utilisez toujours une déclaration à l'aide d'une connexion? Donc, je voudrais l'utiliser sur la connexion, puis un autre sur un lecteur dans la connexion? Donc j'utiliserais deux instructions using.

2) Disons que vous utilisez l'instruction à l'aide sur la connexion et aussi un lecteur étant renvoyé sur la connexion. Donc vous avez deux instructions using. Crée-t-il deux blocs Try {} Finally {} ou un seul?

Merci!

Répondre

4
  1. Vous devriez toujours utiliser une instruction using lorsqu'un objet implémente IDisposable. Cela inclut les connexions.

  2. Il va créer deux try{}finally{} blocs imbriqués.

+0

Vous ne devriez pas utiliser _toujours_ une déclaration 'using' - un cas où des problèmes pourraient survenir lors de l'appel est' Command.ExecuteReader'. Cette méthode peut générer des exceptions, et si c'est le cas, elles ne seront pas gérées correctement et votre logiciel échouera. Mieux vaut gérer les exceptions possibles et appeler manuellement 'Dispose()' ou son équivalent. – Conrad

1

Pour répondre à chacun:

1) Oui, ce serait la meilleure pratique de disposer à la fois le plus tôt possible.

2) using() va créer deux blocs, enveloppé dans l'autre dans le même ordre. Il disposera d'abord l'objet interne (le lecteur), puis disposera l'objet de l'extérieur en utilisant (la connexion).

7

Soyez prudent. Vous devriez toujours une instruction à l'aide sur tout objet local qui implémente IDisposable. Cela inclut non seulement les connexions et les lecteurs, mais aussi la commande. Mais il peut être difficile parfois exactement que l'utilisation de l'instruction va. Si vous ne faites pas attention, cela peut causer des problèmes. Par exemple, dans le code qui suit l'instruction à l'aide fermerons votre lecteur avant de vous jamais l'utiliser:

DataReader MyQuery() 
{ 
    string sql="some query"; 
    using (var cn = new SqlConnection("connection string")) 
    using (var cmd = new SqlCommand(sql, cn)) 
    { 
     cn.Open(); 
     using (var rdr = cmd.ExecuteReader()) 
     { 
      return rdr; 
     } 
    } 
} 

Au lieu de cela, vous disposez de quatre options. L'un est d'attendre pour créer le bloc à l'aide jusqu'à ce que vous appelez la fonction:

DataReader MyQuery() 
{ 
    string sql="some query"; 
    using (var cn = new SqlConnection("connection string")) 
    using (var cmd = new SqlCommand(sql, cn)) 
    { 
     cn.Open(); 
     return cmd.ExecuteReader(); 
    } 
} 

using (var rdr = MyQuery()) 
{ 
    while (rdr.Read()) 
    { 
     //... 
    } 
} 

Bien sûr, vous avez toujours prudent avec votre connexion et il cela signifie se rappeler d'écrire un bloc à l'aide partout où vous utilisez la fonction.

Option deux est tout simplement de traiter les résultats de la requête dans la méthode elle-même, mais qui brise la séparation de votre couche de données du reste du programme. Une troisième option consiste pour votre fonction MyQuery() à accepter un argument de type Action que vous pouvez appeler dans la boucle while (rdr.Read()), mais c'est juste gênant.

Je préfère généralement l'option quatre: tourner le lecteur de données dans un IEnumerable, comme ceci:

IEnumerable<IDataRecord> MyQuery() 
{ 
    string sql="some query"; 
    using (var cn = new SqlConnection("connection string")) 
    using (var cmd = new SqlCommand(sql, cn)) 
    { 
     cn.Open(); 
     using (var rdr = cmd.ExecuteReader()) 
     { 
      while (rdr.Read()) 
       yield return rdr; 
     } 
    } 
} 

Maintenant, tout sera fermé correctement, et le code qui le gère est en un seul endroit. Vous obtenez également un bon bonus: les résultats de votre requête fonctionneront bien avec tous les opérateurs linq.

Enfin, quelque chose de nouveau que je joue avec pour la prochaine fois que je reçois de construire un tout nouveau projet qui combine le IEnumerable avec passage dans un argument délégué:

//part of the data layer 
private static IEnumerable<IDataRecord> Retrieve(string sql, Action<SqlParameterCollection> addParameters) 
{ 
    //DL.ConnectionString is a private static property in the data layer 
    // depending on the project needs, it can be implementing to read from a config file or elsewhere 
    using (var cn = new SqlConnection(DL.ConnectionString)) 
    using (var cmd = new SqlCommand(sql, cn)) 
    { 
     addParameters(cmd.Parameters); 

     cn.Open(); 
     using (var rdr = cmd.ExecuteReader()) 
     { 
      while (rdr.Read()) 
       yield return rdr; 
     } 
    } 
} 

Et puis je vais l'utiliser dans la couche de données comme ceci:

public IEnumerable<IDataRecord> GetFooChildrenByParentID(int ParentID) 
{ 
    //I could easily use a stored procedure name instead, and provide overloads for commandtypes. 
    return Retrieve(
     "SELECT c.* 
     FROM [ParentTable] p 
     INNER JOIN [ChildTable] c ON c.ParentID = f.ID 
     WHERE f.ID= @ParentID", p => 
     { 
      p.Add("@ParentID", SqlDbType.Int).Value = ParentID; 
     } 
    ); 
} 
+2

« Vous devriez toujours avoir aa instruction à l'aide sur un objet qui implémente IDisposable »: bien que ce n'est pas tout à fait vrai ... Vous devez le faire pour les variables ** ** locales qui mettent en œuvre IDisposable, mais pas pour les membres de la classe, car ils généralement être réutilisé ailleurs dans la classe. Cependant, dans ce cas, la classe elle-même devrait mettre en œuvre IDisposable, et éliminer tous les membres IDisposable dans sa méthode Dispose –

+0

Notez également que les classes qui effectuent un « retour de rendement » dans un utilisant ou bloc try/finally mettra en œuvre IDisposable, et non éliminer correctement ils se traduiront par l'utilisation-Dispose ou finalement le code n'étant pas exécuté. – supercat

5

1) Si vous utilisez toujours une déclaration à l'aide une connexion? Donc, je utiliser sur la connexion puis un autre sur un lecteur au sein de la connexion ? Donc j'utiliserais deux en utilisant des instructions.

Oui, car ils implémentent IDisposable. Et ne pas oublier une déclaration using sur la commande aussi:

using (DbConnection connection = GetConnection()) 
using (DbCommand command = connection.CreateCommand()) 
{ 
    command.CommandText = "SELECT FOO, BAR FROM BAZ"; 
    connection.Open(); 
    using (DbDataReader reader = command.ExecuteReader()) 
    { 
     while (reader.Read()) 
     { 
      .... 
     } 
    } 
} 

2) Disons que vous utilisez l'aide de la déclaration sur la connexion et aussi un lecteur étant renvoyé sur la connexion . Vous avez donc deux instructions . Est-il créer deux blocs try {} Enfin {} ou un seul?

Chaque déclaration using va créer son propre bloc try/finally

2

Point spécial sur 1). Vous devez spécifiquement éviter cette technique lorsque la connexion est utilisée dans asynchronous ADO.NET methods - comme BeginExecuteReader, parce que plus que probable, vous tomberez hors de portée et d'essayer de disposer de la connexion pendant que l'opération asynchrone est toujours en cours. Ceci est similaire au cas où vous utilisez des variables de classe et non des variables locales. Souvent, la référence de connexion est stockée dans une classe utilisée comme "bloc de contrôle" pour l'opération asynchrone.