2013-01-31 8 views
6

Aufbau einer Reihe von Berichten zu verringern, haben die gleiche Sache immer und immer wieder mit verschiedenen BereichenGibt es eine Möglichkeit Vervielfältigung in diesen beiden LINQ-Abfragen

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, wie eine leere geht zu tun könnte ich in der Lage sein, diese beiden zusammenzubringen.

+0

Wenn Sie die Erweiterungsmethode (Punktnotation) anstelle der SQL-ähnlichen Abfragenotation verwenden, sehen beide Abfragen gleich aus, mit Ausnahme des Lambda, an dem Sie in die Gruppe übergehen. Mit einem kursorischen Blick denke ich nur die 'Func keySelector' Änderungen, siehe [IEnumerable.GroupBy] (http://msdn.microsoft.com/en-us/library/bb534501 (v = vs. 100) .aspx) auf msdn. Sie hätten also eine 'Func myKeySelector' und der Rest ist gemeinsamer Code. Ich frage mich, ob ich genug Sinn habe, es ist spät und spät und ich bin auf einem neuen OSX, also kann ich momentan kein C# schreiben. – gideon

+0

Sie können die Ausdrucksbaumstruktur verwenden, um dynamische Abfragen zu erstellen. –

Antwort

0

Da sich zwischen den beiden Abfragen nur der Gruppenschlüssel ändert, parametrieren Sie ihn. Da es sich um einen zusammengesetzten Schlüssel handelt (der mehr als einen Wert enthält), müssen Sie eine einfache Klasse erstellen, die diese Werte enthalten kann (mit generischen Namen).

Um diesen zu parametrieren, stellen Sie in diesem Fall den Schlüsselwähler als Parameter für Ihre Funktion ein. Es müsste ein Ausdruck und die Methodensyntax sein, damit dies funktioniert. Sie könnten dann in eine Funktion verallgemeinern:

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); 
} 

Dann nur die Verwendung dieser Funktion in Ihren Anfragen mit dem entsprechenden Schlüsselschalter machen.

Ich weiß nicht, welche Typen Sie für Ihre Entitäten zugeordnet haben, also habe ich einige Annahmen gemacht. Verwenden Sie, was in Ihrem Fall angemessen ist.

1

Es sieht so aus, als ob nur die Namen der Gruppierungsparameter geändert werden. Könnten Sie eine Wrapper-Funktion schreiben, die Lambdas akzeptiert, die die Gruppierungsparameter spezifizieren? Oder sogar eine Wrapper-Funktion, die zwei Strings akzeptiert und dann Raw T-SQL erstellt, anstatt LINQ?

Oder, und ich weiß nicht, ob dies kompilieren würde, können Sie Alias ​​die Felder in der Gruppenanweisung, so dass das Gruppierungskonstrukt immer auf die gleiche Weise referenziert werden kann, wie g.Key.id1 und g.Key.id2? Sie könnten dann das Gruppierungskonstrukt in den ReportSummary Konstruktor übergeben und die Zuordnung links/rechts an einer Stelle vornehmen. (Sie müssen es als dynamisches obwohl passieren, da sie ein anonymes Objekt an der Aufrufstelle)

+0

Der anonyme Typ im 'Select'-Menü variiert ebenfalls. –

+0

Nur in Bezug auf das Gruppierungsobjekt.Die Struktur des Objekts ist in beiden Fällen gleich, so dass ich denke, es könnte verallgemeinert werden (so wie die Antwort von itsme86). –

1

Sie so etwas tun könntest:

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(); 
} 

Und dann könnte man es so nennen:

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

oder:

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

natürlich wollen Sie Record mit whate ersetzen Ver Typ Context.stdReports wird tatsächlich zurückgegeben.

Ich habe nicht überprüft, ob das kompiliert wird, aber Sie bekommen die Idee.

+2

Beachten Sie, dass dies wahrscheinlich ein Abfrageprovider und nicht LINQ to Objects ist Ausdruck > 'anstelle der eigentlichen Funktion, mit der Sie die' IQueryable'-Überladung verwenden können. – Servy

Verwandte Themen