Question

Je ne fais que regarder la déclaration using, j'ai toujours su ce qu'elle fait, mais jusqu'à présent je n'ai pas essayé de l'utiliser, j'ai mis au point le code ci-dessous:

 using (SqlCommand cmd = 
     new SqlCommand(reportDataSource, 
         new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString)))
 {
     cmd.CommandType = CommandType.StoredProcedure;
     cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
     cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
     cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
     cmd.Connection.Open();

     DataSet dset = new DataSet();
     new SqlDataAdapter(cmd).Fill(dset);
     this.gridDataSource.DataSource = dset.Tables[0];
 }

Cela semble fonctionner, mais y a-t-il un intérêt à cela puisque, autant que je sache, il faudrait tout de même inclure cela dans un bloc catch try pour intercepter des erreurs imprévues, par exemple. serveur SQL en panne. Est-ce que je manque quelque chose?

Pour autant que je sache, cela ne fait que m'empêcher de fermer et de disposer de cmd, mais il y aura plus de lignes de code, car le blocage de test est toujours nécessaire.

Était-ce utile?

La solution

Ce code doit être comme suit pour garantir la fermeture rapide de la connexion. Fermer juste la commande ne ferme pas la connexion:

using (SqlConnection con = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, con))
         {
             cmd.CommandType = CommandType.StoredProcedure;
             cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
             cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
             cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
             cmd.Connection.Open();

             DataSet dset = new DataSet();
             new SqlDataAdapter(cmd).Fill(dset);
             this.gridDataSource.DataSource = dset.Tables[0];
         }

Pour répondre à votre question, vous pouvez faire la même chose dans un bloc finally, mais cela permet de bien visualiser le code et de vous assurer de ne pas oublier de nettoyer.

Autres conseils

Lorsque vous travaillez avec l'IO, je code pour une exception .

SqlConnection conn = null;
SqlCommand cmd = null;

try
{
    conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString)
    cmd = new SqlCommand(reportDataSource, conn);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;

        conn.Open(); //opens connection

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}
catch(Exception ex)
{
    Logger.Log(ex);
    throw;
}
finally
{
    if(conn != null)
        conn.Dispose();

        if(cmd != null)
        cmd.Dispose();
}

Modifier: Pour être explicite, j’évite le blocage qui utilise ici, car j’estime important de vous connecter dans de telles situations. L’expérience m’a appris que vous ne savez jamais quel genre d’exception bizarre peut apparaître. Se connecter dans cette situation peut vous aider à détecter une impasse ou à trouver les modifications apportées à un schéma qui affectent une partie peu utilisée et peu testée de votre base de code, ou un certain nombre d'autres problèmes.

Éditer 2: On peut affirmer qu'un bloc using pourrait encapsuler un try / catch dans cette situation, ce qui est tout à fait valide et fonctionnellement équivalent. Cela revient vraiment à la préférence. Voulez-vous éviter la nidification supplémentaire au détriment de la gestion de votre propre élimination? Ou engagez-vous l’imbrication supplémentaire pour une élimination automatique. Je pense que le premier est plus propre, donc je le fais de cette façon. Cependant, je ne réécris pas ce dernier si je le trouve dans la base de code dans laquelle je travaille.

Modifier 3: Je souhaite vraiment que MS ait créé une version plus explicite de using () qui rende plus intuitif ce qui se passe réellement et donne plus de souplesse dans ce cas. Considérez le code imaginaire suivant:

SqlConnection conn = null;
SqlCommand cmd = null;

using(conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString),
          cmd = new SqlCommand(reportDataSource, conn)
{
    conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString);
    cmd = new SqlCommand(reportDataSource, conn);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
        cmd.Open();

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}
catch(Exception ex)
{
    Logger.Log(ex);
    throw;
}

Une instruction using crée simplement un try / finally avec des appels Dispose () dans le finally. Pourquoi ne pas donner au développeur un moyen unifié d’éliminer et de gérer les exceptions?

Il ne peut y avoir aucun avantage à utiliser une instruction à l'aide de dans ce cas, si un essayez / catch / < code> enfin bloquer quand même. Comme vous le savez, l'instruction using est un sucre syntaxique pour un try / finally qui dispose de l'objet IDisposable . Si vous voulez avoir votre propre essayez / enfin , vous pouvez certainement faire le Supprimer vous-même.

Cela se résume essentiellement à du style: votre équipe sera peut-être plus à l'aise avec en utilisant les instructions ou en utilisant peut rendre le code plus net.

Mais, si la phrase utilisant cachée est cachée, allez-y et gérez les choses vous-même si tel est votre préférence.

Si votre code ressemble à ceci:

using (SqlCommand cmd = new SqlCommand(...))
{
  try
  {
    /* call stored procedure */
  }
  catch (SqlException ex)
  {
    /* handles the exception. does not rethrow the exception */
  }
}

Ensuite, je le referrais pour utiliser essayer .. attraper .. enfin à la place.

SqlCommand cmd = new SqlCommand(...)
try
{
  /* call stored procedure */
}
catch (SqlException ex)
{
  /* handles the exception and does not ignore it */
}
finally
{
   if (cmd!=null) cmd.Dispose();
}

Dans ce scénario, je gérerais l'exception, je n'ai donc pas d'autre choix que d'ajouter cet try..catch, je pourrais aussi bien mettre la clause finally et économiser moi-même un autre niveau d'imbrication. Notez que je dois faire quelque chose dans le bloc catch et ne pas ignorer l'exception.

Pour en savoir plus sur ce que Chris Ballance a dit, la section 15.13 de la spécification C # (ECMA-334 version 4) stipule qu'une instruction using est traduite en trois parties: acquisition, utilisation et élimination. L'utilisation de la ressource est implicitement incluse dans une instruction try qui inclut une clause finally. Cette dernière clause dispose de la ressource. Si une ressource nulle est acquise, aucun appel à Dispose n'est effectué et aucune exception n'est levée. "

La description est proche de 2 pages - mérite une lecture.

D'après mon expérience, SqlConnection / SqlCommand peut générer des erreurs de nombreuses manières, de sorte que vous avez presque besoin de gérer les exceptions générées plus que le comportement attendu. Je ne suis pas sûr de vouloir utiliser la clause using ici, car je voudrais pouvoir gérer moi-même le cas de ressource nulle.

utiliser ne consiste pas à intercepter des exceptions. Il s’agit de disposer correctement des ressources qui ne sont pas visibles par le ramasse-miettes.

un problème avec " utiliser " c'est qu'il ne gère pas les exceptions. si les concepteurs de " utiliser " ajouterait " catch " optionnellement à sa syntaxe comme ci-dessous pseudocode, il serait beaucoup plus utile:

using (...MyDisposableObj...)
{

   ... use MyDisposableObj ...

catch (exception)

   ... handle exception ...

}

it could even have an optional "finally" clause to cleanup anything other than the "MyDisposableObj" allocated at the beginning of the "using" statement... like:

using (...MyDisposableObj...)
{

   ... use MyDisposableObj ...
   ... open a file or db connection ...

catch (exception)

   ... handle exception ...

finally

   ... close the file or db connection ...

}

il n'y aura toujours pas besoin d'écrire du code pour se débarrasser de MyDisposableObj car il serait géré par à l'aide de ...

Comment faire comme ça?

Oui, vous devez toujours intercepter des exceptions. L'avantage du bloc using est que vous ajoutez une portée à votre code. Vous dites: "Dans ce bloc de code, effectuez certaines tâches et quand il arrive à la fin, fermez et disposez des ressources"

Ce n'est pas du tout nécessaire, mais cela définit vos intentions à toute autre personne utilisant votre code, et cela permet également de ne pas laisser les connexions, etc. ouvertes par erreur.

Il y a beaucoup de bonnes réponses ici, mais je ne pense pas que cela ait été dit pour le moment.

Peu importe ce que ... l'onglet "Éliminer" La méthode sera appelée sur l'objet dans le champ "Utiliser". bloc. Si vous mettez une instruction return ou générez une erreur, le message "Dispose" sera appelé.

Exemple:

J'ai créé une classe appelée "MyDisposable", qui implémente IDisposable et effectue simplement un script Console.Write. Il écrit toujours sur la console, même dans tous les scénarios suivants:

using (MyDisposable blah = new MyDisposable())
{
    int.Parse("!"); // <- calls "Dispose" after the error.

    return; // <-- calls Dispose before returning.
}

L'instruction using est en fait transformée en un bloc try / finally par le compilateur dans lequel le paramètre du bloc using est supprimé tant qu'il implémente l'interface IDisposable. En plus de vous assurer que les objets spécifiés sont correctement disposés quand ils tombent en dehors de la portée, il n'y a vraiment aucune capture d'erreur obtenue en utilisant cette construction.

Comme mentionné dans TheSoftwareJedi ci-dessus, vous devez vous assurer que les objets SqlConnection et SqlCommand sont tous deux supprimés correctement. Empiler les deux en un seul bloc utilisateur est un peu salissant, et pourrait ne pas faire ce que vous pensez.

Veillez également à utiliser le bloc try / catch comme logique. C’est une odeur de code qui me déplaît particulièrement dans le nez et qui est souvent utilisée par les débutants ou par ceux qui se pressent de respecter le délai imparti.

Pour info, dans cet exemple spécifique, étant donné que vous utilisez une connexion ADO.net et un objet Command, sachez que l’instruction using n’exécute que Command.Dispose et Connection.Dispose () qui ne ferment pas le connexion, mais la libère simplement dans le pool de connexions ADO.net pour être réutilisée par la connexion suivante.open ... ce qui est bien, et la chose tout à fait correcte à faire, bc sinon, la connexion restera inutilisable jusqu'à ce que le ramasse-miettes le libère dans le pool, ce qui risque de ne pas se faire avant de nombreuses autres demandes de connexion, qui seraient sinon obligées de créer de nouvelles connexions même s'il en existe une inutilisée en attente de récupération.

Je déciderais quand et quand ne pas utiliser l'instruction using en fonction de la ressource avec laquelle je traite. Dans le cas d'une ressource limitée, telle qu'une connexion ODBC, je préférerais utiliser T / C / F pour pouvoir enregistrer des erreurs significatives au moment où elles se sont produites. Laisser les erreurs de pilotes de base de données revenir au client et éventuellement être perdues lors du wrapping des exceptions de niveau supérieur est sous optimal.

T / C / F vous assure que la ressource est gérée comme vous le souhaitez. Comme certains l'ont déjà mentionné, l'instruction using ne fournit pas de gestion des exceptions, elle garantit simplement que la ressource est détruite. La gestion des exceptions est une structure de langage sous-estimée et sous-estimée qui fait souvent la différence entre le succès et l'échec d'une solution.

Si l'appelant de votre fonction est responsable du traitement de toutes les exceptions, l'instruction using est un bon moyen de garantir le nettoyage des ressources, quel que soit le résultat.

Il vous permet de placer du code de gestion des exceptions aux limites des couches / assemblages et évite que d'autres fonctions ne deviennent trop encombrées.

Bien sûr, cela dépend vraiment du type d’exception levée par votre code. Parfois, vous devriez utiliser try-catch-finally plutôt qu'une déclaration using. Mon habitude est de toujours commencer par une instruction using pour IDisposables (ou que les classes qui contiennent IDisposables implémentent également l'interface) et d'ajouter try-catch-finally si nécessaire.

Donc, en gros, "en utilisant" correspond exactement à " Essayer / attraper / enfin " seulement beaucoup plus souple pour la gestion des erreurs.

Correction mineure de l'exemple: SqlDataAdapter doit également être instancié dans un à l'aide de l'instruction :

using (SqlConnection con = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, con))
{
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
    con.Open();

    DataSet dset = new DataSet();
    using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
    {
        adapter.Fill(dset);
    }
    this.gridDataSource.DataSource = dset.Tables[0];
}

Tout d'abord, votre exemple de code devrait être:

using (SqlConnection conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, conn))
{
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
    cmd.Connection.Open();

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}

Avec le code de votre question, une exception créant la commande entraînera la suppression de la connexion que vous venez de créer. Avec ce qui précède, la connexion est correctement éliminée.

Si vous devez gérer des exceptions dans la construction de la connexion et de la commande (ainsi que lors de leur utilisation), vous devez envelopper le tout dans un try / catch:

try
{
    using (SqlConnection conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
    using (SqlCommand cmd = new SqlCommand(reportDataSource, conn))
    {
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
        cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
        cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
        cmd.Connection.Open();

        DataSet dset = new DataSet();
        new SqlDataAdapter(cmd).Fill(dset);
        this.gridDataSource.DataSource = dset.Tables[0];
    }
}
catch (RelevantException ex)
{
    // ...handling...
}

Mais vous n'avez pas besoin de gérer le nettoyage de conn ou cmd ; cela a déjà été fait pour vous.

Contrastez avec la même chose sans utilisant :

SqlConnection conn = null;
SqlCommand cmd = null;
try
{
    conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString);
    cmd = new SqlCommand(reportDataSource, conn);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
    cmd.Connection.Open();

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}
catch (RelevantException ex)
{
    // ...handling...
}
finally
{
    if (cmd != null)
    {
        try
        {
            cmd.Dispose();
        }
        catch { }
        cmd = null;
    }
    if (conn != null)
    {
        try
        {
            conn.Dispose();
        }
        catch { }
        conn = null;
    }
}
// And note that `cmd` and `conn` are still in scope here, even though they're useless

Je sais que je préférerais écrire. : -)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top