C# 如何让用户在运行时指定在Linq to Objects GroupBy查询表达式中使用哪些实体属性?
背景故事: 所以我有一个我正在开发的小演示应用程序,它正在浏览歌曲,并试图根据用户指定的标签找到重复的歌曲。最初,我只是使用下面的查询,我试图对其进行概括C# 如何让用户在运行时指定在Linq to Objects GroupBy查询表达式中使用哪些实体属性?,c#,linq,C#,Linq,背景故事: 所以我有一个我正在开发的小演示应用程序,它正在浏览歌曲,并试图根据用户指定的标签找到重复的歌曲。最初,我只是使用下面的查询,我试图对其进行概括 var query = MusicFiles.GroupBy(x => new { x?.Tag.FirstArtist, x?.Tag.Title } ).Where(g => g.Count() > 1) .ToList(); 现在,虽然我想让用户指定这些属性,但我使用反射来获取Tag
var query = MusicFiles.GroupBy(x =>
new { x?.Tag.FirstArtist, x?.Tag.Title }
).Where(g => g.Count() > 1)
.ToList();
现在,虽然我想让用户指定这些属性,但我使用反射来获取TagLib.Tag
类的所有属性,现在允许用户指定他们感兴趣的标记
问题:
我如何使用这个新的字符串列表,通过表示类和组的属性所有这些属性,或者我如何概括上面的代码行
我最初的思路和我尝试的是尝试创建一个匿名对象,如上面所述,并使用反射来获取每个属性,但是,由于我迭代了一个字符串属性列表,因此每个属性都有一个匿名对象,而不是所有选定属性的匿名对象。我只是为透视表样式的报告构建了类似的内容,现在无法与您共享。我的建议是从包含所有可能分组字段的模板group by表达式开始。然后创建一个
ExpressionVisitor
以查找MemberInitExpression
,并删除用户未选择的MemberBinding
。这样,您仍然有一个强类型的groupby表达式,它可以使用efcore的任何特性,而无需重新实现它
表达式模板=x=>
新标记组{x?.Tag.FirstArtist,x?.Tag.Title,…};
公共筛选器:ExpressionVisitor{
公共筛选器(…选择){…}
受保护的重写表达式VisitMemberInit(MemberInitExpression节点){
返回node.Update(node.NewExpression,node.Bindings.Where(…);
}
}
var groupBy=新过滤器(选择)。访问(模板);
填空作为练习。使用
动态Linq
库,该库提供接受字符串参数的Linq扩展方法来指定对象成员,而不是Func
和表达式
,然后遵循本QA中的建议:
它可以通过第三方“维护”版本的Microsoft原始官方System.Linq.Dynamic.dll
程序集在NuGet上以Install Package System.Linq.Dynamic.Core
或Install Package System.Linq.Dynamic
的形式提供,并在GitHub上镜像:(此处提供较旧版本:)
在.NET标准的较新版本中,您需要的扩展方法是
那么您的查询将变成:
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();
使用System.Linq.Dynamic.Core;
[...]
IReadOnlyList userSelectedPropertyList=。。。
//例如,userSelectedPropertyList=new[]{“FirstArtist”,“Title”};
String dynamicLinqGroupByKeySelector=“新建(“+String.Join(“,”,userSelectedPropertyList)+”);
//因此GroupByKeySelector看起来像“新的(第一个艺术家,标题)”。
var query=MusicFiles
.GroupBy(dynamicLinqGroupByKeySelector)
.Where(g=>g.Count()>1)
.ToList();