5

我正在尝试动态重组一些数据以显示在树视图中,这将允许用户选择以下维度中的最多三个来对数据进行分组:

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

这将给出一个这样的结构:

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

但是,这只为我提供了大量组合中的一种。

我将如何将其转换为可以根据用户选择的三个维度动态创建等效表达式的东西,因此我不必为每个组合创建这些表达式中的每一个!!?

多谢你们。

4

2 回答 2

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);
于 2012-05-25T15:28:18.883 回答
0

另一种选择是使用DynamicLinq。如果这是直接的 LINQ(不是通过某些 DB 上下文,例如 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 的良好用例,但我看到的唯一问题是进一步抽象(到内部分组)。

让我们知道您的想法,这绝对是一个我以前没有考虑过的有趣想法。

于 2012-05-25T18:02:08.590 回答