2013-01-31 9 views
6

Costruire un gruppo di rapporti, hanno a che fare la stessa cosa più e più volte con diversi campiC'è un modo per ridurre la duplicazione in questi due LINQ query

public List<ReportSummary> ListProducer() 
    { 
     return (from p in Context.stdReports      
       group p by new { p.txt_company, p.int_agencyId } 
        into g 
        select new ReportSummary 
        { 
         PKi = g.Key.int_agencyId, 
         Name = g.Key.txt_company, 
         Sum = g.Sum(foo => foo.lng_premium), 
         Count = g.Count() 
        }).OrderBy(q => q.Name).ToList(); 
    } 

    public List<ReportSummary> ListCarrier() 
    { 
     return (from p in Context.stdReports 
       group p by new { p.txt_carrier, p.int_carrierId } 
        into g 
        select new ReportSummary 
        { 
         PKi = g.Key.int_carrierId, 
         Name = g.Key.txt_carrier, 
         Sum = g.Sum(foo => foo.lng_premium), 
         Count = g.Count() 
        }).OrderBy(q => q.Name).ToList(); 
    } 

My Mind è il disegno di un vuoto su come potrei essere in grado di riunire questi due

+0

Se si utilizzano i metodi di estensione (notazione punto) anziché la notazione SQL come query, entrambe le query avranno lo stesso aspetto tranne per il lambda che si passa in gruppo per. Con uno sguardo superficiale sto indovinando solo le modifiche di 'Func keySelector', vedi [IEnumerable.GroupBy] (http://msdn.microsoft.com/en-us/library/bb534501 (v = vs. 100) .aspx) su msdn. Quindi avresti un 'Func myKeySelector' e il resto è un codice comune. Mi chiedo se sto dando abbastanza senso, il suo waay a tarda notte e io sono su un nuovo OSX quindi non posso scrivere alcun C# in questo momento. – gideon

+0

È possibile utilizzare la struttura dell'espressione per creare query dinamiche. –

risposta

0

Poiché tutto ciò che cambia tra le due query è la chiave di gruppo, parametrizzarla. Poiché si tratta di una chiave composita (con più di un valore all'interno), è necessario creare una classe semplice che possa contenere tali valori (con nomi generici).

In questo caso, per parametrizzarlo, rendere il selettore a chiave un parametro per la propria funzione. Dovrebbe essere un'espressione e la sintassi del metodo per farlo funzionare. Si potrebbe quindi generalizzare in una funzione:

public class GroupKey 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

private IQueryable<ReportSummary> GetReport(
    Expression<Func<stdReport, GroupKey>> groupKeySelector) 
{ 
    return Context.stdReports 
     .GroupBy(groupKeySelector) 
     .Select(g => new ReportSummary 
     { 
      PKi = g.Key.Id, 
      Name = g.Key.Name, 
      Sum = g.Sum(report => report.lng_premium), 
      Count = g.Count(), 
     }) 
     .OrderBy(summary => summary.Name); 
} 

Poi basta fare uso di questa funzione nelle query utilizzando i selettori a chiave appropriate.

public List<ReportSummary> ListProducer() 
{ 
    return GetReport(r => 
     new GroupKey 
     { 
      Id = r.int_agencyId, 
      Name = r.txt_company, 
     }) 
     .ToList(); 
} 

public List<ReportSummary> ListCarrier() 
{ 
    return GetReport(r => 
     new GroupKey 
     { 
      Id = r.int_carrierId, 
      Name = r.txt_carrier, 
     }) 
     .ToList(); 
} 

Non so quali tipi che hai mappato per le entità così ho fatto alcune ipotesi. Usa quello che è appropriato nel tuo caso.

1

Sembra che l'unica cosa che cambia sono i nomi dei parametri di raggruppamento. Potresti scrivere una funzione wrapper che accetti lambda specificando i parametri di raggruppamento? O anche una funzione wrapper che accetta due stringhe e quindi crea T-SQL raw, invece di usare LINQ?

Oppure, e non so se questo verrà compilato, è possibile alias i campi nell'istruzione di gruppo in modo che il costrutto di raggruppamento possa sempre essere referenziato allo stesso modo, ad esempio g.Key.id1 e g.Key.id2? È quindi possibile passare il costrutto di raggruppamento nel costruttore ReportSummary e eseguire l'assegnazione sinistra/mano destra in un unico punto. (Avresti bisogno di passare come dinamica, però, dal momento che la sua un oggetto anonimo al sito di chiamata)

+0

Anche il tipo anonimo in "Seleziona" varia. –

+0

Solo nel modo in cui si riferisce all'oggetto di raggruppamento.La struttura dell'oggetto è la stessa in entrambi i casi, quindi penso che potrebbe essere generalizzata (un po 'come la risposta di itsme86) –

1

Si potrebbe fare qualcosa di simile:

public List<ReportSummary> GetList(Func<Record, Tuple<string, int>> fieldSelector) 
{ 
    return (from p in Context.stdReports      
     group p by fieldSelector(p) 
      into g 
      select new ReportSummary 
      { 
       PKi = g.Key.Item2 
       Name = g.Key.Item1, 
       Sum = g.Sum(foo => foo.lng_premium), 
       Count = g.Count() 
      }).OrderBy(q => q.Name).ToList(); 
} 

E allora si potrebbe chiamare in questo modo:

var summary = GetList(rec => Tuple.Create(rec.txt_company, rec.int_agencyId)); 

o:

var summary = GetList(rec => Tuple.Create(rec.txt_carrier, rec.int_carrierId)); 

Naturalmente, si vorrà sostituire Record con whate ver il tipo Context.stdReports restituisce effettivamente.

Non ho controllato per vedere se questo verrà compilato, ma si ottiene l'idea.

+2

Si noti che poiché questo è probabilmente un provider di query e non LINQ per gli oggetti che si desidera passare in un ' Espressione > 'al posto della funzione effettiva che ti consente di utilizzare l'overload' IQueryable'. – Servy

Problemi correlati