gpt4 book ai didi

c# - 按表达式树进行多列分组

转载 作者:行者123 更新时间:2023-12-02 11:02:29 25 4
gpt4 key购买 nike

根据帖子LINQ Expression of the Reference Property感谢 Daniel Hilgarth 的帮助,我已经实现了 Group By Extension,我需要帮助来扩展 GroupByMany,如下所示

_unitOfWork.MenuSetRepository.Get().GroupBy("Role.Name","MenuText");

扩展方法

public static IEnumerable<IGrouping<string, TElement>> GroupBy<TElement>(this IEnumerable<TElement> elements,string property)
{
var parameter = Expression.Parameter(typeof(TElement), "groupCol");
Expression<Func<TElement, string>> lambda;
if (property.Split('.').Count() > 1)
{
Expression body = null;
foreach (var propertyName in property.Split('.'))
{
Expression instance = body;
if (body == null)
instance = parameter;
body = Expression.Property(instance, propertyName);
}
lambda = Expression.Lambda<Func<TElement, string>>(body, parameter);
}
else
{
var menuProperty = Expression.PropertyOrField(parameter, property);
lambda = Expression.Lambda<Func<TElement, string>>(menuProperty, parameter);
}

var selector= lambda.Compile();
return elements.GroupBy(selector);
}

最佳答案

这个答案由两部分组成:

  1. 为您的问题提供解决方案
  2. 向您介绍 IEnumerable<T>IQueryable<T>以及两者的区别

第 1 部分:解决您当前问题的解决方案

新要求并不像其他要求那么容易满足。主要原因是按组合键分组的 LINQ 查询会导致在编译时创建匿名类型:

source.GroupBy(x => new { x.MenuText, Name = x.Role.Name })

这会产生一个具有编译器生成的名称和两个属性的新类 MenuTextName .
在运行时执行此操作是可能的,但实际上并不可行,因为它会涉及将 IL 发送到新的动态程序集中。

对于我的解决方案,我选择了不同的方法:
因为所有涉及的属性似乎都是 string 类型我们分组所依据的键只是用分号分隔的属性值的串联。
因此,我们的代码生成的表达式相当于以下内容:

source.GroupBy(x => x.MenuText + ";" + x.Role.Name)

实现此目的的代码如下所示:

private static Expression<Func<T, string>> GetGroupKey<T>(
params string[] properties)
{
if(!properties.Any())
throw new ArgumentException(
"At least one property needs to be specified", "properties");

var parameter = Expression.Parameter(typeof(T));
var propertyExpressions = properties.Select(
x => GetDeepPropertyExpression(parameter, x)).ToArray();

Expression body = null;
if(propertyExpressions.Length == 1)
body = propertyExpressions[0];
else
{
var concatMethod = typeof(string).GetMethod(
"Concat",
new[] { typeof(string), typeof(string), typeof(string) });

var separator = Expression.Constant(";");
body = propertyExpressions.Aggregate(
(x , y) => Expression.Call(concatMethod, x, separator, y));
}

return Expression.Lambda<Func<T, string>>(body, parameter);
}

private static Expression GetDeepPropertyExpression(
Expression initialInstance, string property)
{
Expression result = null;
foreach(var propertyName in property.Split('.'))
{
Expression instance = result;
if(instance == null)
instance = initialInstance;
result = Expression.Property(instance, propertyName);
}
return result;
}

这又是我在 previous 中展示的方法的扩展two答案。

其工作原理如下:

  1. 对于每个提供的深层属性字符串,通过 GetDeepPropertyExpression 获取相应的表达式。这基本上就是我在之前的答案中添加的代码。
  2. 如果只传递了一个属性,则直接将其用作 lambda 的主体。结果与我之前的答案中的表达式相同,例如x => x.Role.Name
  3. 如果传递了多个属性,我们会将这些属性相互连接并在其之间使用分隔符,并将其用作 lambda 的主体。我选择了分号,但你可以使用任何你想要的。假设我们传递了三个属性( "MenuText", "Role.Name", "ActionName" ),那么结果将如下所示:

    x => string.Concat(
    string.Concat(x.MenuText, ";", x.Role.Name), ";", x.ActionName)

    这与 C# 编译器为使用加号连接字符串的表达式生成的表达式相同,因此等价于:

    x => x.MenuText + ";" + x.Role.Name + ";" + x.ActionName

第 2 部分:教育您

您在问题中展示的扩展方法是一个非常糟糕的主意。
为什么?嗯,因为它适用于 IEnumerable<T> 。这意味着该 group by 不是在数据库服务器上执行,而是在应用程序的内存中本地执行。此外,后面的所有 LINQ 子句,例如 Where也在内存中执行!

如果您想提供扩展方法,则需要为 IEnumerable<T> 执行此操作(在内存中,即 LINQ to Objects)和 IQueryable<T> (适用于要在数据库上执行的查询,例如 LINQ to Entity Framework)。
微软也选择了同样的方法。对于大多数 LINQ 扩展方法,存在两种变体: 一种适用于 IEnumerable<T>以及适用于 IQueryable<T> 的一个生活在两个不同的类(class) Enumerable Queryable 。比较这些类中方法的第一个参数。

所以,你想做的是这样的:

public static IEnumerable<IGrouping<string, TElement>> GroupBy<TElement>(
this IEnumerable<TElement> source, params string[] properties)
{
return source.GroupBy(GetGroupKey<TElement>(properties).Compile());
}

public static IQueryable<IGrouping<string, TElement>> GroupBy<TElement>(
this IQueryable<TElement> source, params string[] properties)
{
return source.GroupBy(GetGroupKey<TElement>(properties));
}

关于c# - 按表达式树进行多列分组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17680840/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com