2009-05-19 7 views
0

La méthode suivante est relativement simple. J'essaie de déterminer un taux d'éléments de ligne en faisant correspondre une autre propriété de l'élément de ligne avec une recherche d'un objet parent. Il y a quelques petites choses que je n'aime pas et je cherche des solutions élégantes pour rendre la méthode plus petite, plus efficace, ou les deux. Cela fonctionne dans son état actuel et ce n'est pas comme si c'était inefficace ou quoi que ce soit. Ce n'est pas critique de la mission ou quoi que ce soit, plus d'une curiosité.Nettoyage d'un foreach simple avec linq

private decimal CalculateLaborTotal() 
    { 
     decimal result = 0; 

     foreach (ExtraWorkOrderLaborItem laborItem in Labor) 
     { 
      var rates = (from x in Project.ContractRates where x.ProjectRole.Name == laborItem.ProjectRole.Name select x).ToList(); 
      if (rates != null && rates.Count() > 0) 
      { 
       result += laborItem.Hours * rates[0].Rate; 
      } 
     } 
     return result; 
    } 

J'aime l'idée d'utiliser List<T>.ForEach(), mais j'avais du mal à garder un peu assez succinct pour être encore facile à lire/maintenir. Des pensées?

+0

Nous pourrions probablement faire quelques suppositions, mais il serait beaucoup plus facile d'aider si nous avons compris les objets avec lesquels vous travailliez, et pourquoi vous référencez "rates [0] .Rate" pour chaque calcul. –

+0

var rates = (à partir de x dans Project.ContractRates où x.ProjectRole.Name == laborItem.ProjectRole.Name sélectionnez x.Rate) .FirstOrDefault(); serait probablement mieux – Keltex

+0

Ah, FirstOrDefault, c'est une chose que je cherchais (mais pas très longtemps). Je n'aimais vraiment pas devoir convertir cette requête en une liste, puis référencer le premier objet via l'index. – nkirkes

Répondre

4

Quelque chose comme ça devrait le faire (non testé!):

var result = 
    (from laborItem in Labor 
    let rate = (from x in Project.ContractRates 
       where x.ProjectRole.Name == laborItem.ProjectRole.Name 
       select x).FirstOrDefault() 
    where rate != null 
    select laborItem.Hours * rate.Rate).Sum(); 

Or (en supposant qu'un seul taux peut correspondre) une jointure serait encore plus net:

var result = 
    (from laborItem in Labor 
    join rate in Project.ContractRates 
     on laborItem.ProjectRole.Name equals rate.ProjectRole.Name 
    select laborItem.Hours * rate.Rate).Sum(); 
+0

Oui, un seul taux devrait correspondre. J'aime l'option de jointure, je vais jouer avec ça un peu. – nkirkes

+0

Je sais que cette question est plutôt subjective, mais j'ai vraiment aimé comment la jointure dans votre réponse a rendu mon code plus succinct tout en maintenant la lisibilité. Je suis nouveau à linq et c'était un bon exemple à suivre. – nkirkes

0

Omettre la vérification rates != null - Une requête linq peut être vide mais pas null. Si vous avez juste besoin du premier élément d'une liste, utilisez List.First ou List.FirstOrDefault.

Il n'y a aucun avantage à utiliser .

2

D'accord, bien que diriez-vous:

// Lookup from name to IEnumerable<decimal>, assuming Rate is a decimal 
var ratesLookup = Project.ContractRates.ToLookup(x => x.ProjectRole.Name, 
               x => x.Rate); 

var query = (from laborItem in Labor 
      let rate = ratesGroup[laborItem].FirstOrDefault() 
      select laborItem.Hours * rate).Sum(); 

L'avantage ici est que vous n'avez pas besoin de regarder à travers une liste potentiellement importante des taux contractuels chaque fois - une fois que vous construisez la recherche. Ce n'est peut-être pas un problème, bien sûr.