2012-05-25 1 views
5

Я пытаюсь динамически повторно структуру некоторые данные, которые будут показаны в виде дерева, что позволяет будет пользователю выбрать до трех следующих размеров группировки данных по:Создание GroupBy Заявления Динамически

Organisation 
Company 
Site 
Division 
Department 

Так, например, если пользователь должен был выбрать, что они хотели бы сгруппировать Компанией, тогда Сайт затем Отдел ... следующий код выполнит необходимые группировки.

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

Это дало бы structre так:

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

Однако, это только дает мне на большое число комбинаций.

Как я мог бы преобразовать это во что-то, что могло бы создать эквивалентное выражение динамически на основе трех измерений, которые выбрал пользователь, и поэтому мне не нужно создавать одно из этих выражений для каждой комбинации! ?

Спасибо, ребята.

ответ

4

Незначительная проблема. Выбор одного типа для группировки ключей и другого типа для результатов ... позволяет получить то, о чем вы просите.

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

} 

Используется следующим образом:

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

Другой подход состоит в том, чтобы сделать 5 классов Grouper (по одному для каждого типа группировки) с общим интерфейсом IGroupEntities {List GetItems (IEnumerable )}, чтобы они могли звонить друг другу. –

+0

Спасибо за этот ответ! Я пробовал что-то подобное, но, должно быть, отсутствовала ссылка, так как я до сих пор не закончил эту итерацию. Я использовал ваш образец как стартер и закодировал решение, которое отлично работает для того, что я пытался сделать. Большое спасибо!!! – Penfold

+0

+100 если бы я мог! Отличный ответ! –

0

Другой вариант заключается в использовании DynamicLinq. Если это прямой LINQ (не через какой-то БД контекста, такие как LINQ2SQL), то это может быть сделано путем составления ваших группирования строк/селектора:

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

Вы, вероятно, абстрактное это в каждый из типов критериев. Единственная проблема, которую я вижу, заключается в том, что внутренние группы не могут быть проще всего скомпилировать, но, по крайней мере, это может помочь вам начать работу в определенном направлении. DynamicLinq позволяет вам создавать динамические типы, поэтому его можно еще более абстрагировать. В конечном счете, самая большая проблема заключается в том, что на основе того, что вы группируете, генерируемый TreeViewItem содержит различную информацию. Хороший вариант использования для динамического LINQ, но единственная проблема, которую я вижу, абстрагируется еще дальше по линии (во внутренние группы).

Сообщите нам, что вы придумали, определенно интересная идея, которую я раньше не рассматривал.

Смежные вопросы