2012-05-25 23 views
5

sto cercando di ristrutturare dinamicamente alcuni dati da visualizzare in una visualizzazione struttura che sarà permette all'utente di selezionare fino a tre delle seguenti dimensioni raggruppare i dati da:Crea GroupBy bilancio dinamicamente

Organisation 
Company 
Site 
Division 
Department 

Ad esempio, se l'utente dovesse selezionare di voler raggruppare per società, quindi sito, divisione ... il codice seguente eseguirà i raggruppamenti richiesti.

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

Questo darebbe uno structre come questo:

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

Tuttavia, questo mi fornisce solo su di un gran numero di combinazioni.

Come farei per convertire questo in qualcosa che potrebbe creare l'espressione equivalente in modo dinamico in base alle tre dimensioni che l'utente ha scelto e quindi non devo creare una di ciascuna di queste espressioni per ciascuna combinazione !! ?

Grazie ragazzi.

risposta

4

Un problema intrigante. Scegliere un singolo tipo per raggruppare le chiavi e un altro tipo per i risultati ... rende molto possibile ottenere ciò che stai chiedendo.

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

} 

utilizzato in questo modo:

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

Un altro approccio consiste nel rendere 5 classi Grouper (una per ogni tipo di raggruppamento) con un'interfaccia comune IGroupEntities {List GetItems (IEnumerable )} in modo che possano chiamarsi a vicenda. –

+0

Grazie per questa risposta! Avevo provato qualcosa di simile ma doveva esserci un collegamento mancante perché non avevo mai completato questa iterazione. Ho usato il tuo campione come antipasto e ho codificato una soluzione che funziona perfettamente per quello che stavo cercando di fare. Grazie mille!!! – Penfold

+0

+100 se potessi! Bella risposta! –

0

Un'altra opzione è quella di utilizzare DynamicLinq. Se questo è LINQ dritto (non attraverso qualche contesto DB come LINQ2SQL), allora questo può essere fatto componendo i tuoi raggruppamento/stringhe di selezione:

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... 

Probabilmente si può astratto questo in ogni di tipo criteri. L'unico problema che vedo è che i raggruppamenti interni potrebbero non essere i più facili da compilare insieme, ma almeno questo può farti iniziare in qualche direzione. DynamicLinq ti permette di costruire tipi dinamici, quindi è certamente possibile astrarlo ulteriormente. In definitiva, la sfida più grande è quella basata su ciò che stai raggruppando, il TreeViewItem generato contiene informazioni diverse. Buon caso d'uso per LINQ dinamico, ma l'unico problema che vedo è l'astrazione ancora più in basso (verso i raggruppamenti interni).

Facci sapere cosa ne pensi, sicuramente un'idea interessante che non avevo considerato prima.

Problemi correlati