2010-09-01 8 views
1

Le code suivant me donne cette erreur d'analyse de codeErreur CA2000 avec l'instruction 'using'. Comment rendre cela valide?

CA2000: Microsoft.Reliability: Dans la méthode 'SessionSummary.SessionSummary_Load (objet, EventArgs)', appeler des 'entités' System.IDisposable.Dispose sur un objet avant tout les références à celui-ci sont hors du champ d'application.

J'utilise un « utilisant » déclaration, donc je suis surpris sur ce point:

private void SessionSummary_Load(object sender, EventArgs e) 
    { 
     using (var entities = new DbEntities(Properties.Settings.Default.UserConnectionString)) 
     { 
      entities.CommandTimeout = 7200; 
      var sessions = from t in entities.TableName 
          where t.UserSession.Id == _id && t.Parent == 0 
          group t by new { t.UserSession, t.UserSession.SessionId } into sessionGroup 
          select new 
          { 
           Id = sessionGroup.Key.UserSession,         
           Session = sessionGroup.Key.SessionId         
          }; 

      summaryDataGridView.DataSource = sessions.Where(x => x.Time > 0.00); 
      summaryDataGridView.Columns[4].DefaultCellStyle.Format = "N2"; 
      summaryDataGridView.Columns[4].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; 
     } 
    } 

Répondre

3

Vous effectuez une requête de style LINQ sur entitiesmais ne sont pas dénombraient en fait sur le résultat au sein le bloc using. Cela crée un problème de fermeture, puisque les informations de requête stockées dans sessions.Where(x => x.Time > 0.00) sont stockées dans summaryDataGridView.DataSource et qu'il y a donc une référence à entities toujours en mémoire après la sortie de votre code ci-dessus.

L'explication est que les méthodes LINQ fournissent deferred execution *, ce qui signifie que ni session, ni la valeur que vous affectez à summaryDataGridView.DataSource sera évaluée dans le code ci-dessus.

Pour forcer l'évaluation, vous devriez être en mesure de le faire:

summaryDataGridView.DataSource = sessions.Where(x => x.Time > 0.00).ToList(); 

L'ajout de ToList() ci-dessus en fait la cause de votre requête à exécuter et les résultats à mises en mémoire cache. De plus, entities sera hors de portée et vous n'aurez plus de références à travers une fermeture; donc je crois qui devrait faire l'affaire pour vous.

* Note: c'était juste le premier lien que j'ai trouvé d'une recherche de Google. Cela semblait plutôt bien.

+0

Cela ressemble à une bonne pratique, mais il ne s'est pas débarrassé de l'avertissement CA en ajoutant ToList(). Des idées? – esac

+0

@esac: Je me demande si la réduction de la requête en une seule instruction ferait l'affaire? Essayez: 'var sessions = [/*...ce que vous avez ... * /] .Where (x => x.Time> 0.00) .ToList();' Et puis juste 'summaryDataGridView.DataSource = sessions;' Est-ce que ça marche? –

+0

Non, mis à jour comme vous l'avez dit, et toujours obtenir la même erreur. – esac

4

Vous avez en fait une élimination potentiellement prématurée, plutôt qu'une élimination tardive, car les entités impliquées dans la fermeture assignée à la source de données signifient qu'elle quitte la portée.

Vous avez alors avez une entité dans la nature qui ne sera pas éliminée dans sa portée réelle, ce dont se plaint l'analyse, mais cela ne devrait pas poser de problème par rapport au fait qu'elle est disposé avant sa dernière utilisation.

Options:

  1. le laisser tel qu'il est. Si ce qui précède fonctionne, il est probablement car l'élimination n'affecte pas l'état nécessaire pour que la fermeture fonctionne. C'est risqué. Parier sur "probablement" n'est pas une bonne idée, et peut également changer sur la route. (Je peux penser à un cas où l'utilisation d'un objet après l'élimination est logique, mais c'est obscur et pas ce que vous avez ici de toute façon).

  2. Forcer l'exécution de la requête avec impatience. L'appel ToList() ou ToArray() sur la requête l'exécutera et créera un résultat en mémoire qui est ensuite utilisé comme source de données. Au mieux, cependant, cela sera moins efficace dans l'espace et le temps. Au pire, il pourrait être paralysant (en fonction de la taille des résultats que vous avez affaire).

  3. Assurez-vous que le contrôle se termine à l'aide de sa source de données avant de quitter l'étendue. Puis effacez la source de données. Selon le contrôle en question et d'autres questions (en particulier, s'il a une méthode explicite DataBind()), il peut être trivial, impossible ou quelque part entre les deux.

  4. Mettez l'entité dans une variable d'instance. Mettre en œuvre IDisposable. Dans votre méthode Dispose(), appelez sa méthode Dispose(). N'ajoutez pas de finaliseur pour cela, car vous ne disposez que d'un objet géré.

  5. Créez une méthode énumérable qui enveloppe la requête (et l'utilisation), puis exécute un yield return sur chaque élément renvoyé par la requête. Utilisez ceci comme source de données.

5 semble le meilleur choix pour la plupart des cas. Il a l'avantage de ne pas modifier le code tout en ne rajoutant pas le préfixe (potentiellement grand, en fonction des données) du numéro 2. Notez qu'il suffit d'appeler AsEnumerable (qui a presque le même effet sur l'ordre d'exécution) n'aurait pas le même effet, car la fermeture laisserait toujours le bloc non exécuté.

Edit: Une dénombrable qui enveloppe la requête serait comme:

private IEnumerable GetSessions() 
{ 
    using (var entities = new DbEntities(Properties.Settings.Default.UserConnectionString)) 
    { 
     entities.CommandTimeout = 7200; 
     var sessions = from t in entities.TableName 
         where t.UserSession.Id == _id && t.Parent == 0 
         group t by new { t.UserSession, t.UserSession.SessionId } into sessionGroup 
         select new 
         { 
          Id = sessionGroup.Key.UserSession,         
          Session = sessionGroup.Key.SessionId         
         }; 

     foreach(var sess in sessions.Where(x => x.Time > 0.00)) 
      yield return sess; 
    } 
} 

Ensuite, vous définissez le changement SessionSummary_Load à:

private void SessionSummary_Load(object sender, EventArgs e) 
{ 
     summaryDataGridView.DataSource = GetSessions(); 
     summaryDataGridView.Columns[4].DefaultCellStyle.Format = "N2"; 
     summaryDataGridView.Columns[4].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; 
    } 
} 

Espérons que cela va résoudre le problème, parce que entities ne quitte jamais la portée du using.

+0

Je ne suis pas sûr de comprendre ce que vous entendez par "une méthode enumerable qui enveloppe la requête et l'utilisation" – esac

+0

@esac a ajouté un peu à ma réponse pour le détailler plus complètement. –

+0

Je vais essayer comme expérience, mon seul souci est que je ne veux pas créer 100 méthodes différentes pour chaque requête linq que je fais. – esac