动态创建 GroupBy 语句

本文关键字:语句 GroupBy 创建 动态 | 更新日期: 2023-09-27 18:35:17

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

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

但是,这仅为我提供了大量组合。

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

谢谢大家。

动态创建 GroupBy 语句

一个有趣的问题。 选择一种类型来分组键,选择另一种类型来分组结果...使获得您想要的东西是很有可能的。

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

另一种选择是使用 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 的良好用例,但我看到的唯一问题是进一步抽象(到内部分组)。

让我们知道你想出了什么,绝对是一个我以前从未考虑过的有趣想法。