Question

Ma question concerne l'état de la connexion SQL, la charge, etc. en fonction du code suivant:

public IEnumberable<MyType> GetMyTypeObjects()
{
  string cmdTxt = "select * from MyObjectTable";

  using(SqlConnection conn = new SqlConnection(connString))
  {
    using(SqlCommand cmd = new SqlCommand(cmdTxt, conn))
    {
      conn.Open();
      using(SqlDataReader reader = cmd.ExecuteReader())
      {
         while(reader.Read())
         {
            yield return Mapper.MapTo<MyType>(reader);
         }
       }
    }
  }
  yield break;
}

Cela peut poser problème si de nombreux processus exécutent un code similaire avec des temps d’exécution longs entre les itérations de l’objet IEnumerable, car les connexions seront ouvertes plus longtemps, etc. Cependant, il semble également plausible que cela réduise l'utilisation de l'UC sur le serveur SQL car il ne renvoie des données que lorsque l'objet IEnumerable est utilisé. Cela réduit également l'utilisation de la mémoire sur le client car le client n'a qu'à charger une instance de MyType pendant qu'il fonctionne plutôt que de charger toutes les occurrences de MyType (en effectuant une itération dans l'intégralité de DataReader et en renvoyant une liste ou autre).

  • Y a-t-il des cas où vous ne voudriez pas utiliser IEnumerable de cette manière, ou des cas où vous pensez que cela convient parfaitement?

  • Quel type de charge cela met-il sur le serveur SQL?

  • Utilisez-vous cela dans votre propre code (sauf mention de NHibernate, Subsonic, etc.)?

  •  -
Était-ce utile?

La solution

Je ne l'emploierais pas, car il cache ce qui se passe et pourrait éventuellement laisser des connexions à une base de données sans élimination appropriée.

L'objet de connexion sera fermé lorsque vous aurez lu après le dernier enregistrement. Si vous arrêtez de lire avant, l'objet de connexion ne sera pas supprimé. Si, par exemple, vous savez que vous avez toujours dix enregistrements dans un résultat et qu'une boucle lit ces dix enregistrements de l'énumérateur sans passer le onzième appel Read lu au-delà du dernier élément, la connexion n'est pas fermée correctement. De même, si vous ne souhaitez utiliser qu'une partie du résultat, vous ne pouvez pas fermer la connexion sans lire le reste des enregistrements.

Même les extensions intégrées pour les énumérateurs peuvent être la cause même si vous les utilisez correctement:

foreach (MyType item in GetMyTypeObjects().Take(10)) {
   ...
}

Autres conseils

Ce n’est pas un schéma que je suivrais. Je ne m'inquiéterais pas autant de la charge sur le serveur que des verrous. Suivre ce modèle intègre le processus de récupération des données dans votre flux de logique métier, ce qui semble être une recette complète pour résoudre les problèmes; vous n'avez aucune idée de ce qui se passe du côté de l'itération et vous vous y insérez. Récupérez vos données en une fois, puis laissez le code client y énumérer une fois que vous avez fermé le lecteur.

Je déconseille la pré-optimisation. Dans de nombreuses situations, les connexions seront mises en commun.

Je ne m'attends pas non plus à une différence de charge sur SQL Server - la requête aura déjà été compilée et sera en cours d'exécution.

Mon souci est que vous vous mettiez complètement à la merci du code client.

Vous avez déjà mentionné que le code d'appel peut maintenir la connexion ouverte plus longtemps qu'il n'est strictement nécessaire, mais il est également possible qu'il ne permette jamais aux objets d'être fermés / supprimés.

Tant que le code client utilise pour chaque , avec , etc. ou appelle explicitement la méthode Dispose

var e = GetMyTypeObjects().GetEnumerator();
e.MoveNext();    // open the connection etc

// forget about the enumerator and go away and do something else
// now the reader, command and connection won't be closed/disposed
// until the GC kicks in and calls their finalisers
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top