在 .net 核心中使用表达式的动态 GroupBy
Dynamic GroupBy using expression in .net core
我写了一个扩展方法,根据条件做一个表达式,但是当条件是groupby时,结果是order by !!!
我哪里错了?
这是我的方法:
public static IQueryable<T> NewWhere<T, U>(this IQueryable<T> source, string prop, U value, string condition)
{
MethodInfo method;
Expression<Func<T, bool>> lambda = null;
Expression body = null;
string groupSelector = null;
var type = typeof(T);
var parameter = Expression.Parameter(type, "p");
var property = Expression.Property(parameter, prop);
var constant = Expression.Constant(value, typeof(U));
if (condition == "GreaterThan")
body = Expression.GreaterThan(property, constant);
else if (condition == "LessThan")
body = Expression.LessThan(property, constant);
else if (condition == "Equals")
body = Expression.Equal(property, constant);
//For implement sql like command we need them
else if (condition == "StartsWith") {
method = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
body = Expression.Call(property, method, constant);
}
else if (condition == "EndsWith")
{
method = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
body = Expression.Call(property, method, constant);
}
else if (condition == "Contains")
{
method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
body = Expression.Call(property, method, constant);
}
//For implement sql like command we need them
//group by only one field
if (condition == "GroupBy")
groupSelector = prop;
else
lambda = Expression.Lambda<Func<T, bool>>(body, new[] { parameter });
//return the grouped by result or not grouped by
if (groupSelector != null)
{
var selectorExp = Expression.Lambda<Func<T, U>>(property, new ParameterExpression[] { parameter });
source = source.GroupBy(selectorExp).SelectMany(g => g);
//source.GroupBy(e => e.GetType().GetProperty(groupSelector)).SelectMany(gr => gr);
}
else
source = source.Where(lambda);
return source;
}
但是当我运行有GroupBy条件的方法时,结果是:
SELECT [e].[Id], [e].[Year]
FROM [org].[Data] AS [e]
ORDER BY [e].[Year]
我不知道为什么会这样?
TL;DR;: Entitity Framework 使用查询,因为它是获得所需内容的最有效方式。
Entity Framework 所做的是将您的 LINQ 查询转换为 SQL。检查你的这行代码:
source = source.GroupBy(selectorExp).SelectMany(g => g);
您正在分组(可能按年份),然后您 select 该组的所有项目。您实际上并不是在请求分组的结果集,您期望所有组中的所有项目 在一个单一的平面结果集中 。如果 EF 首先请求组,然后请求组项,则它首先必须 select 所有组:
SELECT [e].[Year]
FROM [org].[Data] AS [e]
GROUP BY [e].[Year]
然后它必须在一个查询中获取组项目每个组:
SELECT [e].[Id]
FROM [org].[Data] AS [e]
WHERE [e].[Year] = --Value
那当然是非常低效的(特别是因为你无论如何都要用 SelectMany
展平列表),所以 EF 将只获取你的分组谓词排序的值并在查询执行期间(或在此情况下根本不要将它们分组,因为您正在请求一个平面列表)。一旦执行查询,EF可以直接从结果集的顶部开始,每次遇到新的年份就开始一个新的组。
当您使用 EF 查询时,您必须接受您无法控制 SQL。如果需要,请从 EF 创建存储过程并 运行 它们。
我写了一个扩展方法,根据条件做一个表达式,但是当条件是groupby时,结果是order by !!! 我哪里错了?
这是我的方法:
public static IQueryable<T> NewWhere<T, U>(this IQueryable<T> source, string prop, U value, string condition)
{
MethodInfo method;
Expression<Func<T, bool>> lambda = null;
Expression body = null;
string groupSelector = null;
var type = typeof(T);
var parameter = Expression.Parameter(type, "p");
var property = Expression.Property(parameter, prop);
var constant = Expression.Constant(value, typeof(U));
if (condition == "GreaterThan")
body = Expression.GreaterThan(property, constant);
else if (condition == "LessThan")
body = Expression.LessThan(property, constant);
else if (condition == "Equals")
body = Expression.Equal(property, constant);
//For implement sql like command we need them
else if (condition == "StartsWith") {
method = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
body = Expression.Call(property, method, constant);
}
else if (condition == "EndsWith")
{
method = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
body = Expression.Call(property, method, constant);
}
else if (condition == "Contains")
{
method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
body = Expression.Call(property, method, constant);
}
//For implement sql like command we need them
//group by only one field
if (condition == "GroupBy")
groupSelector = prop;
else
lambda = Expression.Lambda<Func<T, bool>>(body, new[] { parameter });
//return the grouped by result or not grouped by
if (groupSelector != null)
{
var selectorExp = Expression.Lambda<Func<T, U>>(property, new ParameterExpression[] { parameter });
source = source.GroupBy(selectorExp).SelectMany(g => g);
//source.GroupBy(e => e.GetType().GetProperty(groupSelector)).SelectMany(gr => gr);
}
else
source = source.Where(lambda);
return source;
}
但是当我运行有GroupBy条件的方法时,结果是:
SELECT [e].[Id], [e].[Year]
FROM [org].[Data] AS [e]
ORDER BY [e].[Year]
我不知道为什么会这样?
TL;DR;: Entitity Framework 使用查询,因为它是获得所需内容的最有效方式。
Entity Framework 所做的是将您的 LINQ 查询转换为 SQL。检查你的这行代码:
source = source.GroupBy(selectorExp).SelectMany(g => g);
您正在分组(可能按年份),然后您 select 该组的所有项目。您实际上并不是在请求分组的结果集,您期望所有组中的所有项目 在一个单一的平面结果集中 。如果 EF 首先请求组,然后请求组项,则它首先必须 select 所有组:
SELECT [e].[Year]
FROM [org].[Data] AS [e]
GROUP BY [e].[Year]
然后它必须在一个查询中获取组项目每个组:
SELECT [e].[Id]
FROM [org].[Data] AS [e]
WHERE [e].[Year] = --Value
那当然是非常低效的(特别是因为你无论如何都要用 SelectMany
展平列表),所以 EF 将只获取你的分组谓词排序的值并在查询执行期间(或在此情况下根本不要将它们分组,因为您正在请求一个平面列表)。一旦执行查询,EF可以直接从结果集的顶部开始,每次遇到新的年份就开始一个新的组。
当您使用 EF 查询时,您必须接受您无法控制 SQL。如果需要,请从 EF 创建存储过程并 运行 它们。