2012-05-25 21 views
5

Ich versuche, einige Daten zu dynamisch neu Struktur in einer Baumstruktur dargestellt werden, die der Benutzer zu drei der folgenden Dimensionen Gruppe durch die Daten, um bis erlaubt:erstellen GroupBy Statements dynamisch

Organisation 
Company 
Site 
Division 
Department 

Wenn der Benutzer zum Beispiel auswählen würde, dass er nach Firma, dann Standort, dann Abteilung ... gruppieren möchte, würde der folgende Code die erforderlichen Gruppierungen durchführen.

var entities = orgEntities 
// Grouping Level 1 
.GroupBy(o => new { o.CompanyID, o.CompanyName }) 
.Select(grp1 => new TreeViewItem 
     { 
     CompanyID = grp1.Key.CompanyID, 
     DisplayName = grp1.Key.CompanyName, 
     ItemTypeEnum = TreeViewItemType.Company, 
     SubItems = grp1 
       // Grouping Level 2 
       .GroupBy(o => new { o.SiteID, o.SiteName }) 
       .Select(grp2 => new TreeViewItem 
       { 
       SiteID = grp2.Key.SiteID, 
       DisplayName = grp2.Key.SiteName, 
       ItemTypeEnum = TreeViewItemType.Site, 
       SubItems = grp2 
        // Grouping Level 3 
        .GroupBy(o => new { o.Division }) 
        .Select(grp3 => new TreeViewItem 
        { 
         DisplayName = grp3.Key.Division, 
         ItemTypeEnum = TreeViewItemType.Division, 
        }).ToList() 
       }).ToList() 
     }) 
.ToList(); 

Dies würde eine structre wie folgt geben:

+ Company A 
    + Site A 
    + Division 1 
    + Division 2 
    + Site B 
    + Division 1 
+ Company B 
    + Site C 
    + Division 2 
+ Company C 
    + Site D 

dies stellt jedoch nur mich mit auf einer großen Anzahl von Kombinationen.

Wie würde ich dies in etwas konvertieren, das den äquivalenten Ausdruck dynamisch basierend auf den drei vom Benutzer gewählten Dimensionen erstellen könnte, sodass ich keinen der Ausdrücke für jede Kombination erstellen muss !! ?

Danke Jungs.

Antwort

4

Ein faszinierendes Problem. Die Auswahl eines einzigen Typs für die Gruppierung von Schlüsseln und eines anderen Typs für Ergebnisse ... macht es möglich, das zu bekommen, wonach Sie fragen.

public struct EntityGroupKey 
{ 
    public int ID {get;set;} 
    public string Name {get;set;} 
} 

public class EntityGrouper 
{ 
    public Func<Entity, EntityGroupKey> KeySelector {get;set;} 
    public Func<EntityGroupKey, TreeViewItem> ResultSelector {get;set;} 
    public EntityGrouper NextGrouping {get;set;} //null indicates leaf level 

    public List<TreeViewItem> GetItems(IEnumerable<Entity> source) 
    { 
    var query = 
     from x in source 
     group x by KeySelector(x) into g 
     let subItems = NextGrouping == null ? 
     new List<TreeViewItem>() : 
     NextGrouping.GetItems(g) 
     select new { Item = ResultSelector(g.Key), SubItems = subItems }; 

    List<TreeViewItem> result = new List<TreeViewItem>(); 
    foreach(var queryResult in query) 
    { 
      // wire up the subitems 
     queryResult.Item.SubItems = queryResult.SubItems 
     result.Add(queryResult.Item); 
    } 
    return result; 
    } 

} 

auf diese Weise verwendet:

EntityGrouper companyGrouper = new EntityGrouper() 
{ 
    KeySelector = o => new EntityGroupKey() {ID = o.CompanyID, Name = o.CompanyName}, 
    ResultSelector = key => new TreeViewItem 
    { 
    CompanyID = key.ID, 
    DisplayName = key.Name, 
    ItemTypeEnum = TreeViewItemType.Company 
    } 
} 

EntityGrouper divisionGrouper = new EntityGrouper() 
{ 
    KeySelector = o => new EntityGroupKey() {ID = 0, Name = o.Division}, 
    ResultSelector = key => new TreeViewItem 
    { 
    DisplayName = key.Name, 
    ItemTypeEnum = TreeViewItemType.Division 
    } 
} 

companyGrouper.NextGrouping = divisionGrouper; 

List<TreeViewItem> oneWay = companyGrouper.GetItems(source); 

companyGrouper.NextGrouping = null; 
divisionGrouper.NextGrouping = companyGrouper; 

List<TreeViewItem> otherWay = divisionGrouper.GetItems(source); 
+0

Ein weiterer Ansatz besteht darin, 5 Grouper-Klassen (eine für jede Art von Gruppierung) mit einer gemeinsamen Schnittstelle IGroupEntities {List GetItems (IEnumerable )} zu erstellen, damit sie sich gegenseitig aufrufen können. –

+0

Danke für diese Antwort! Ich hatte etwas Ähnliches versucht, aber es musste einen fehlenden Link gegeben haben, da ich diese Iteration nie zum Abschluss brachte. Ich habe Ihre Probe als Starter verwendet und eine Lösung programmiert, die perfekt für das funktioniert, was ich versucht habe. Vielen Dank!!! – Penfold

+0

+100 wenn ich könnte! Gute Antwort! –

0

Eine weitere Option ist DynamicLinq zu verwenden.

var entities = orgEntities 
    .GroupBy("new(CompanyID, CompanyName)", "it", null) // DynamicLinq uses 'it' to reference the instance variable in lambdas. 
    .Select(grp1 => new TreeViewItem 
    { 
     ... 
     .GroupBy("new(SiteID, o.SiteName)", "it", null) 
     // And so on... 

Sie können sich wahrscheinlich abstrakt diese in jede der Kriterientyp: Wenn diese gerade LINQ (nicht durch einige DB Kontext wie LINQ2SQL), dann kann dies durch Komponieren Ihrer Gruppierung/Selektor-Strings erfolgen. Das einzige Problem, das ich sehe, ist, dass die inneren Gruppierungen möglicherweise nicht am einfachsten zusammen zu kompilieren sind, aber zumindest kann dies in eine Richtung beginnen. Mit DynamicLinq können Sie dynamische Typen erstellen, sodass es möglich ist, sie noch weiter zu abstrahieren. Letztendlich besteht die größte Herausforderung darin, dass das generierte TreeViewItem basierend auf dem, nach dem Sie gruppieren, unterschiedliche Informationen enthält. Guter Anwendungsfall für dynamisches LINQ, aber das einzige Problem, das ich sehe, ist das Abstrahieren noch weiter unten (zu den inneren Gruppierungen).

Lassen Sie uns wissen, was Sie haben, auf jeden Fall eine interessante Idee, die ich vorher nicht berücksichtigt hatte.