dans un bloc «using», un SqlConnection est-il fermé au retour ou à une exception?

136

Première question:
dis que j'ai

using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();

    string storedProc = "GetData";
    SqlCommand command = new SqlCommand(storedProc, connection);
    command.CommandType = CommandType.StoredProcedure;
    command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));

    return (byte[])command.ExecuteScalar();
}

La connexion est-elle fermée? Parce que techniquement, nous n'arrivons jamais au dernier }comme returnavant.

Deuxième question:
Cette fois, j'ai:

try
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        int employeeID = findEmployeeID();

        connection.Open();
        SqlCommand command = new SqlCommand("UpdateEmployeeTable", connection);
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));
        command.CommandTimeout = 5;

        command.ExecuteNonQuery();
    }
}
catch (Exception) { /*Handle error*/ }

Maintenant, disons quelque part dans le trynous obtenons une erreur et elle est interceptée. La connexion est-elle toujours fermée? Car encore une fois, nous sautons le reste du code dans le tryet passons directement à l' catchinstruction.

Est-ce que je pense trop linéairement à la façon dont usingfonctionne? c'est-à-dire est Dispose()simplement appelé lorsque nous quittons la usingportée?

Marcus
la source

Réponses:

178
  1. Oui
  2. Oui.

Dans tous les cas, lorsque le bloc utilisateur est quitté (soit par réussite, soit par erreur), il est fermé.

Bien que je pense qu'il serait préférable de s'organiser comme ça, car il est beaucoup plus facile de voir ce qui va se passer, même pour le nouveau programmeur de maintenance qui le soutiendra plus tard:

using (SqlConnection connection = new SqlConnection(connectionString)) 
{    
    int employeeID = findEmployeeID();    
    try    
    {
        connection.Open();
        SqlCommand command = new SqlCommand("UpdateEmployeeTable", connection);
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));
        command.CommandTimeout = 5;

        command.ExecuteNonQuery();    
    } 
    catch (Exception) 
    { 
        /*Handle error*/ 
    }
}
David
la source
3
@TrueWill - Je suis d'accord. J'ai juste déplacé un peu le code pour la structure.
David
10
Question: Dois-je même ouvrir une connexion lorsque j'utilise l'instruction Using?
Fandango68 du
3
Aussi, si vous utilisez des transactions, en ayant le try catchdans le, usingvous pouvez explicitement .Commitou des .Rollbacktransactions dans le catch. Ceci est à la fois plus lisible et explicite, et vous permet de valider si cela a du sens étant donné le type de l'exception. (Les transactions sont implicitement annulées conn.Closesi elles ne sont pas validées.).
Chris
8
@ Fernando68 Oui, vous avez toujours Openla connexion. usinggarantit seulement que la Disposeméthode de l'objet est appelée.
juharr
J'ai le retour ExecuteScalar à l'intérieur en utilisant des blocs. Et quand j'exécute la méthode la deuxième fois, c'est très rapide, comme si la connexion était ouverte. Pourquoi est-ce si rapide la deuxième fois?
perspective positive
46

Oui aux deux questions. L'instruction using est compilée dans un bloc try / finally

using (SqlConnection connection = new SqlConnection(connectionString))
{
}

est le même que

SqlConnection connection = null;
try
{
    connection = new SqlConnection(connectionString);
}
finally
{
   if(connection != null)
        ((IDisposable)connection).Dispose();
}

Edit: Correction de la distribution en jetable http://msdn.microsoft.com/en-us/library/yh598w02.aspx

Ryan Pedersen
la source
ce n'est pas exactement cela, mais c'est assez proche. la différence exacte n'est pas importante.
Bryan
@Bryan ne l'a pas compris, pouvez-vous s'il vous plaît mentionner la différence exacte, peut nous aider à nous pencher davantage :-)
mohits00691
Wow, c'était un commentaire fait il y a longtemps :) On dirait qu'il y a eu une modification le lendemain de mon commentaire. Je pense que c'est la différence à laquelle je pensais.
Bryan
@Bryan Oui, j'ai corrigé l'ajustement après votre commentaire.
Ryan Pedersen
17

Voici mon modèle. Tout ce dont vous avez besoin pour sélectionner des données à partir d'un serveur SQL. La connexion est fermée et supprimée et les erreurs de connexion et d'exécution sont interceptées.

string connString = System.Configuration.ConfigurationManager.ConnectionStrings["CompanyServer"].ConnectionString;
string selectStatement = @"
    SELECT TOP 1 Person
    FROM CorporateOffice
    WHERE HeadUpAss = 1 AND Title LIKE 'C-Level%'
    ORDER BY IntelligenceQuotient DESC
";
using (SqlConnection conn = new SqlConnection(connString))
{
    using (SqlCommand comm = new SqlCommand(selectStatement, conn))
    {
        try
        {
            conn.Open();
            using (SqlDataReader dr = comm.ExecuteReader())
            {
                if (dr.HasRows)
                {
                    while (dr.Read())
                    {
                        Console.WriteLine(dr["Person"].ToString());
                    }
                }
                else Console.WriteLine("No C-Level with Head Up Ass Found!? (Very Odd)");
            }
        }
        catch (Exception e) { Console.WriteLine("Error: " + e.Message); }
        if (conn.State == System.Data.ConnectionState.Open) conn.Close();
    }
}

* Révisé: 2015-11-09 *
Tel que suggéré par NickG; Si trop d'accolades vous ennuient, formatez comme ça ...

using (SqlConnection conn = new SqlConnection(connString))
   using (SqlCommand comm = new SqlCommand(selectStatement, conn))
   {
      try
      {
         conn.Open();
         using (SqlDataReader dr = comm.ExecuteReader())
            if (dr.HasRows)
               while (dr.Read()) Console.WriteLine(dr["Person"].ToString());
            else Console.WriteLine("No C-Level with Head Up Ass Found!? (Very Odd)");
      }
      catch (Exception e) { Console.WriteLine("Error: " + e.Message); }
      if (conn.State == System.Data.ConnectionState.Open) conn.Close();
   }

Là encore, si vous travaillez pour des jeux EA ou DayBreak, vous pouvez également renoncer aux sauts de ligne, car ceux-ci sont réservés aux personnes qui doivent revenir et regarder votre code plus tard et qui s'en soucient vraiment? Ai-je raison? Je veux dire 1 ligne au lieu de 23 signifie que je suis un meilleur programmeur, non?

using (SqlConnection conn = new SqlConnection(connString)) using (SqlCommand comm = new SqlCommand(selectStatement, conn)) { try { conn.Open(); using (SqlDataReader dr = comm.ExecuteReader()) if (dr.HasRows) while (dr.Read()) Console.WriteLine(dr["Person"].ToString()); else Console.WriteLine("No C-Level with Head Up Ass Found!? (Very Odd)"); } catch (Exception e) { Console.WriteLine("Error: " + e.Message); } if (conn.State == System.Data.ConnectionState.Open) conn.Close(); }

Ouf ... OK. J'ai sorti ça de mon système et j'ai fini de m'amuser pendant un moment. Continuer.

ShaneLS
la source
6
Saviez-vous que vous pouvez empiler à l'aide d'instructions sans accolades supplémentaires? Supprimez la dernière accolade, puis placez les instructions using les unes à côté des autres :)
NickG
Oui monsieur. Je vous remercie. Je suis conscient, mais je voulais que mon code montre exactement ce qui se passait sans utiliser trop d'autres raccourcis. Bonne note à ajouter aux lecteurs finaux.
ShaneLS
Pourquoi utilisez-vous conn.Close();à la fin? La usingdéclaration ne fait- elle pas cela pour vous via l'élimination?
Fredrick Gauss
Je crois que c'est le cas maintenant (depuis .net 3.5). Ce n'était pas clair pour moi au début avec .net 2.0, alors j'ai juste pris l'habitude de vérifier et de fermer.
ShaneLS
1
"signifie 1 ligne au lieu de 23 signifie que je suis un meilleur programmeur, non?" Je t'aime bien :-D
Philipp Müller
5

Dispose est simplement appelé lorsque vous quittez le champ d'utilisation. L'intention de «l'utilisation» est de donner aux développeurs un moyen garanti de s'assurer que les ressources sont éliminées.

Depuis MSDN :

Une instruction using peut être quittée lorsque la fin de l'instruction using est atteinte ou si une exception est levée et que le contrôle quitte le bloc d'instructions avant la fin de l'instruction.

surentendu
la source
5

Usinggénère un essai / enfin autour de l'objet alloué et appelle Dispose()pour vous.

Cela vous évite d'avoir à créer manuellement le bloc try / finally et à appeler Dispose()

Bébé vaudou
la source
3

Dans votre premier exemple, le compilateur C # traduira en fait l'instruction using en ce qui suit:

SqlConnection connection = new SqlConnection(connectionString));

try
{
    connection.Open();

    string storedProc = "GetData";
    SqlCommand command = new SqlCommand(storedProc, connection);
    command.CommandType = CommandType.StoredProcedure;
    command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));

    return (byte[])command.ExecuteScalar();
}
finally
{
    connection.Dispose();
}

Enfin, les instructions seront toujours appelées avant le retour d'une fonction et ainsi la connexion sera toujours fermée / supprimée.

Ainsi, dans votre deuxième exemple, le code sera compilé comme suit:

try
{
    try
    {
        connection.Open();

        string storedProc = "GetData";
        SqlCommand command = new SqlCommand(storedProc, connection);
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));

        return (byte[])command.ExecuteScalar();
    }
    finally
    {
        connection.Dispose();
    }
}
catch (Exception)
{
}

L'exception sera interceptée dans l'instruction finally et la connexion sera fermée. L'exception ne sera pas vue par la clause catch externe.

Kerri Brown
la source
1
très bons exemples homme, mais je ne suis pas d'accord sur votre dernier commentaire, si une exception se produit dans un bloc using, elle sera interceptée sans problème sur aucune capture externe, en fait je l'ai testé en écrivant 2 blocs en utilisant un bloc try / catch , et à ma grande surprise, j'ai obtenu mon message d'erreur d'exception qui provenait de la seconde interne en utilisant le bloc.
WhySoSerious
1

J'ai écrit deux instructions using dans un bloc try / catch et je pouvais voir que l'exception était interceptée de la même manière si elle était placée dans l' instruction using interne , comme dans l' exemple ShaneLS .

     try
     {
       using (var con = new SqlConnection(@"Data Source=..."))
       {
         var cad = "INSERT INTO table VALUES (@r1,@r2,@r3)";

         using (var insertCommand = new SqlCommand(cad, con))
         {
           insertCommand.Parameters.AddWithValue("@r1", atxt);
           insertCommand.Parameters.AddWithValue("@r2", btxt);
           insertCommand.Parameters.AddWithValue("@r3", ctxt);
           con.Open();
           insertCommand.ExecuteNonQuery();
         }
       }
     }
     catch (Exception ex)
     {
       MessageBox.Show("Error: " + ex.Message, "UsingTest", MessageBoxButtons.OK, MessageBoxIcon.Error);
     }

Peu importe où est placé le try / catch , l'exception sera interceptée sans problème.

Pourquoi si sérieux
la source