我已经解决了你的问题,但没有得到最终令人满意的结果。我只列出我能找到并理解的几点。
1)
我重写了你的最后一个代码片段(以简化的形式,没有投影到匿名类型)...
var query = from c in Countries
select FindSeverity(u => u.Building.Country == c).Max();
...然后在扩展方法语法中:
var query = Countries
.Select(c => FindSeverity(u => u.Building.Country == c).Max());
现在我们看得更清楚了FindSeverity(u => u.Building.Country == c).Max()
is the body of an Expression<Func<Country, T>>
(T
is int
在这种情况下)。 (我不确定“body”是否是正确的技术终点,但你知道我的意思:Lambda 箭头右侧的部分=>)。当整个查询被转换为表达式树时,该主体被转换为对函数的方法调用FindSeverity
。 (当您观看时,您可以在调试器中看到这一点Expression
的财产query
: FindSeverity
直接是表达式树中的一个节点,而不是此方法的主体。)这在执行时失败,因为 LINQ to Entities 不知道此方法。在此类 lambda 表达式的主体中,您只能使用已知函数,例如 static 中的规范函数System.Data.Objects.EntityFunctions
class.
2)
构建查询的可重用部分的一种可能的通用方法是编写以下自定义扩展方法IQueryable<T>
, 例如:
public static class MyExtensions
{
public static IQueryable<int?> FindSeverity(this IQueryable<User> query,
Expression<Func<User, bool>> predicate)
{
var last30Days = DateTime.Today.AddDays(-30);
return from u in query.Where(predicate)
from i in u.Issues
where i.Date > last30Days
select i.Severity;
}
}
然后你可以编写如下查询:
var max1 = Users.FindSeverity(u => u.Building.ID == 1).Max();
var max2 = Users.FindSeverity(u => u.Building.Country == "Wonderland").Max();
正如您所看到的,您被迫使用扩展方法语法编写查询。我没有看到在查询语法中使用此类自定义查询扩展方法的方法。
上面的示例只是创建可重用查询片段的通用模式,但它对您问题中的特定查询并没有真正的帮助。至少我不知道如何重新表述你的FindSeverity
方法,使其适合这种模式。
3)
我相信您的原始查询无法在 LINQ to Entities 中运行。像这样的查询...
from b in Building
let issueSeverity = (from u in Users
where u.Building == b
from i in u.Issues
where i.Date > last30Days
select i.Severity).Max()
select new
{
Building = b,
IssueSeverity = issueSeverity
}
...属于该类别“引用非标量变量” http://msdn.microsoft.com/en-us/library/bb896317.aspx#RefNonScalarClosuresLINQ to Entities 不支持的查询内部。 (在 LINQ to Objects 中它可以工作。)上面查询中的非标量变量是Users
。如果Building
表不为空,预计会出现异常:“无法创建 EntityType 类型的常量值。在此上下文中仅支持原始类型(例如 Int32、String 和 Guid)。”
看来您之间存在一对多关系User
and Building
在数据库中,但此关联并未在您的实体中完全建模:User
具有导航属性Building
but Building
没有集合Users
。在这种情况下,我期望Join
在查询中,类似:
from b in Building
join u in Users
on u.Building.ID equals b.ID
let issueSeverity = (i in u.Issues
where i.Date > last30Days
select i.Severity).Max()
select new
{
Building = b,
IssueSeverity = issueSeverity
}
这不会创建提到的引用非标量变量的异常。但也许我误解了你的模型。