【发布时间】:2018-01-24 19:38:38
【问题描述】:
我试图回答this question但失败了:
那么让我们以原始查询为例:
var result = db.Employees.GroupBy(x => x.Region)
.Select(g => new { Region = g.Key,
Avg = g.Average(x => x.BaseSalary)});
工作正常。现在我们要动态地决定要平均什么。我尝试动态创建 Average 的 lambda:
string property = "BaseSalary";
var parameter = Expression.Parameter(typeof(Employee));
var propAccess = Expression.PropertyOrField(parameter, property);
var expression = (Expression<Func<Employee,int?>>)Expression.Lambda(propAccess, parameter);
var lambda = expression.Compile();
并使用它:
var result = db.Employees.GroupBy(x => x.Region)
.Select(g => new { Region = g.Key,
Avg = g.Average(lambda)});
使用 Linq2Sql,这会导致 NotSupportedException:
Für den Abfrageoperator "Average" wurde eine nicht unterstützte Überladung verwendet。
(我只有德语错误信息,上面写着不支持Average 使用的重载,如果您有英文版,请随时编辑)。
原题使用了Linq2Entities,报错
内部 .NET Framework 数据提供程序错误 102
IntelliSense(或其他一些 IDE 功能)告诉我,在两个版本中,编译器都选择了Average 的相同的重载:
double? Enumerable.Average(this IEnumerable<Employee> source, Func<Employee, int?> selector);
我用ExpressionVisitor 重新检查了我的lambda 与x => x.BaseSalary 的表达式完全相同。
那么:为什么突然不再支持它了?
有趣:如果我不分组并简单地使用它,则没有这样的例外:
double? result = db.Employees.Average(lambda);
对于YuvalShap's answer,我还尝试了Avg = g.AsQueryable().Average(expression)(使用表达式而不是lambda),但结果相同。
【问题讨论】:
-
通常
AsQueryable()技巧在 EF6 中有效。我们在这里谈论的是 EF Core 吗? -
@IvanStoev 现在我遇到了麻烦:最初的问题(链接在顶部)是关于 EF 的,但我只在 linq2sql 中复制,因为它更容易/更快地设置。所以我真的很感兴趣为什么 linq2sql 会失败,特别是因为在这两个版本中,
Average(x => x.BaseSalaray和lambda)的参数是我可以确定的完全相同的东西。但是原来的提问者还是会对 EF 感兴趣,只是不知道是不是 EF Core(其实我完全不习惯 ef)。 -
这取决于查询翻译器是否识别并处理表达式树中的
AsQueryable方法调用。 EF6 有,EF Core 目前没有,根据您的评论 LINQ to SQL 也没有。因此,该技术是不可靠的。因此可以使用一些类似于当前答案的自定义表达式技巧,但通常需要一些表达式组合库,如 LINQKitAsExpandable/Invoke。 -
@IvanStoev 虽然这可能会解决问题(至少对于 EF6),但我实际上问是因为我不明白为什么
x => x.BaseSalary有效并且(恕我直言)samelambda没有。但我现在意识到整个Select参数是一个表达式树,所以x => x.BaseSalary还没有编译,而是由提供者分析的。lambda是提供程序不再可解析的局部变量。因此AsQueryable将起作用(如果提供者认可)。奇怪的是异常保持不变而不是抱怨AsQueryable()。
标签: c# linq-to-sql linq-to-entities expression-trees