【问题标题】:How can I let users specify which entity properties are used in a Linq-to-Objects GroupBy query expression at runtime?如何让用户指定在运行时在 Linq-to-Objects GroupBy 查询表达式中使用哪些实体属性?
【发布时间】:2019-12-23 23:46:16
【问题描述】:

背景故事:

所以我有一个我正在开发的小演示应用程序,它正在查看歌曲并尝试根据用户指定的标签查找重复项。最初我只是使用下面的查询,我试图概括。

var query = MusicFiles.GroupBy(x => 
        new { x?.Tag.FirstArtist, x?.Tag.Title }
    ).Where(g => g.Count() > 1)
    .ToList();

现在虽然我想让用户指定这些属性,所以我使用反射来获取 TagLib.Tag 类的所有属性,现在允许用户指定他们感兴趣的标签。

问题:

我如何使用这个新的字符串列表来表示类的属性并按所有这些属性进行分组,或者如何概括上面的代码行。

我最初的思路和尝试是尝试创建一个像上面这样的匿名对象并使用反射来获取每个属性,但是由于我正在迭代字符串属性列表,因此我最终得到了一个每个属性的匿名对象,而不是所有选定属性的匿名对象

【问题讨论】:

    标签: c# linq


    【解决方案1】:

    使用Dynamic-Linq 库,它提供了接受字符串参数的Linq 扩展方法来指定对象成员,而不是Func<...>Expression<Func<...>>,然后遵循此QA 中的建议:How to use GroupBy using Dynamic LINQ

    它可以通过第三方“维护”版本的微软原始官方System.Linq.Dynamic.dll 程序集作为Install-Package System.Linq.Dynamic.CoreInstall-Package System.Linq.Dynamic 在 NuGet 上使用,并在 GitHub 上镜像:https://github.com/StefH/System.Linq.Dynamic.Core/wiki/Dynamic-Expressions(此处提供旧版本:@ 987654323@)

    在 .NET Standard 的最新版本中,您想要的扩展方法是 GroupBy( this IQueryable source, String keySelector, params Object\[\] args )

    那么您的查询将变为:

    using System.Linq.Dynamic.Core;
    
    [...]
    
    IReadOnlyList<String> userSelectedPropertyList = ...
    // e.g. userSelectedPropertyList = new[] { "FirstArtist", "Title" };
    
    String dynamicLinqGroupByKeySelector = "new (" + String.Join( ", ", userSelectedPropertyList ) + ")";
    // so the GroupBy keySelector looks like "new (FirstArtist, Title)".
    
    var query = MusicFiles
        .GroupBy( dynamicLinqGroupByKeySelector )
        .Where( g => g.Count() > 1 )
        .ToList();
    

    【讨论】:

      【解决方案2】:

      我刚刚为数据透视表样式报告构建了类似的东西,我现在无法与您分享。我的建议是从包含所有可能的分组字段的表达式开始模板分组。然后创建一个ExpressionVisitor 以查找MemberInitExpression,并删除用户没有选择的任何MemberBinding。这样,您仍然拥有一个强类型的表达式组,它可以使用 EF Core 的任何功能,而无需重新实现。

      Expression<Func<X,TagGroup>> template = x => 
              new TagGroup { x?.Tag.FirstArtist, x?.Tag.Title, ... };
      
      public Filter : ExpressionVisitor{
          public Filter(... selection){ ... }
          protected override Expression VisitMemberInit(MemberInitExpression node){
              return node.Update(node.NewExpression, node.Bindings.Where( ... ));
          }
      }
      
      var groupBy = new Filter(selection).Visit(template);
      

      填空作为练习。

      【讨论】:

        猜你喜欢
        • 2011-10-24
        • 1970-01-01
        • 1970-01-01
        • 2011-10-22
        • 2021-05-28
        • 1970-01-01
        • 1970-01-01
        • 2019-05-16
        • 1970-01-01
        相关资源
        最近更新 更多