2009-06-19 6 views
1

J'ai récemment écrit une requête LINQ pour obtenir un Dictionary contenant les montants de placement des 6 derniers mois.Comment rendre cette requête LINQ plus propre?

Il renvoie une chaîne Dictionary de mois - paires de nombres décimaux.

Cela semble un peu compliqué. N'importe lequel d'entre vous maîtrisant LINQ peut-il m'aider à refactoriser cela pour le rendre un peu plus propre?

/// <summary> 
/// Gets the last 6 months of Placement History totalled by Month 
/// for all Agencies 
/// </summary> 
/// <returns></returns> 
public Dictionary<string, decimal> getRecentPlacementHistory() 
{ 
    var placementHistoryByMonth = new Dictionary<string, decimal>(); 

    using (DemoLinqDataContext db = new DemoLinqDataContext()) 
    { 
     for (int i = 0; i < 6; i++) 
     { 
      Decimal monthTotal = 
       (from a in db.Accounts 
       where 
       (a.Date_Assigned.Value.Month == DateTime.Now.AddMonths(-i).Month && 
        a.Date_Assigned.Value.Year == DateTime.Now.AddMonths(-i).Month) 
       select a.Amount_Assigned).Sum(); 
      String currentMonth = DateTime.Now.AddMonths(-i).ToString("MMM"); 

      placementHistoryByMonth.Add(currentMonth, monthTotal); 
     } 
     return placementHistoryByMonth; 
    } 
} 

Répondre

5

Premier problème:

where (a.Date_Assigned.Value.Month == DateTime.Now.AddMonths(-i).Month && 
     a.Date_Assigned.Value.Year == DateTime.Now.AddMonths(-i).Month) 

devrait-il pas cette dernière expression se terminer par .an plutôt que .Month? Vous obtiendrez rarement une année avec une valeur de 1 à 12 ...

Je voudrais extraire l'idée du "mois courant" comme vous l'utilisez beaucoup. Notez que vous aussi prendre le temps en cours à plusieurs reprises, ce qui pourrait donner des résultats bizarres si elle tourne à minuit à la fin d'un mois ...

public Dictionary<string, decimal> getRecentPlacementHistory() 
{ 
    var placementHistoryByMonth = new Dictionary<string, decimal>(); 
    using (DemoLinqDataContext db = new DemoLinqDataContext()) 
    { 
     DateTime now = DateTime.Now; 

     for (int i = 0; i < 6; i++) 
     { 
      DateTime selectedDate = now.AddMonths(-i); 

      Decimal monthTotal = 
       (from a in db.Accounts 
       where (a.Date_Assigned.Value.Month == selectedDate.Month && 
         a.Date_Assigned.Value.Year == selectedDate.Year) 
       select a.Amount_Assigned).Sum(); 

      placementHistoryByMonth.Add(selectedDate.ToString("MMM"), 
             monthTotal); 
     } 
     return placementHistoryByMonth; 
    } 
} 

Je me rends compte qu'il est probablement la boucle que vous essayez de se débarrasser de. Vous pouvez essayer de travailler sur les limites supérieure et inférieure des dates pour le lot entier, puis de regrouper par année/mois de a.Date_Assigned dans les limites pertinentes. Ce ne sera pas beaucoup plus joli, pour être honnête. Rappelez-vous, ce ne serait qu'une requête à la base de données, si vous pouviez le retirer.

+1

selectedDate.Month.Year n'a pas beaucoup de sens ... faute de frappe? ;) – em70

+0

@Jon Skeet, Comment puis-je gérer la situation où le mois est Janvier. Une fois que nous soustrayons des mois de la date actuelle, l'année ne correspondra plus et cette méthode échouera. J'essaie de comprendre comment contourner cela, tout en ne correspondant pas pour chaque Janvier. –

+0

Oui, je me demandais à ce sujet. Il utilise selectedDate.Month.Year pour éviter d'obtenir des données de tous les mois similaires au cours des années, mais si la boucle de la plage de dates soustrait suffisamment de mois, l'année changera sur lui et cela ne fonctionnera pas. Comment voulez-vous contourner cela? – KingNestor

0

Si vous n'êtes pas inquiet de manquer mois sans données, alors j'eu un problème similaire où je l'ai fait ce qui suit: (traduit à vos variables)

DateTime startPeriod = 
    new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1).AddMonths(-6); 

    var query1 = from a in db.Accounts where a.Date_Assigned >= startPeriod 
group a by new { a.Date_Assigned.Year ,a.Date_Assigned.Month } into result 
select new 
{ 
    dt = new DateTime(result.Key.Year, result.Key.Month , 1), 
    MonthTotal = result.Sum(i => i.Amount_Assigned) 
} ;    

    var dict = query1.OrderBy(p=> p.dt).ToDictionary(n => n.Dt.ToString("MMM") , n => n.MonthTotal); 
2

Usegroup Par

DateTime now = DateTime.Now; 
DateTime thisMonth = new DateTime(now.Year, now.Month, 1); 

Dictionary<string, decimal> dict; 
using (DemoLinqDataContext db = new DemoLinqDataContext()) 
{ 
    var monthlyTotal = from a in db.Accounts 
     where a.Date_Assigned > thisMonth.AddMonths(-6) 
     group a by new {a.Date_Assigned.Year, a.Date_Assigned.Month} into g 
     select new {Month = new DateTime(g.Key.Year, g.Key.Month, 1), 
        Total = g.Sum(a=>a.Amount_Assigned)}; 

    dict = monthlyTotal.OrderBy(p => p.Month).ToDictionary(n => n.Month.ToString("MMM"), n => n.Total); 
} 

Aucune boucle nécessaire!

+0

Nice! Il peut être utile de grouper par (Année * 100 + Mois). En outre, précalculer "thisMonth.AddMonths (-6)" – Ray

Questions connexes