如何从较小的可重复使用的查询中编写entity framework查询?

我在我的应用程序中有一些(相当多余的)查询,如下所示:

var last30Days = DateTime.Today.AddDays(-30); 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 } 

和:

 var last30Days = DateTime.Today.AddDays(-30); from c in Countries let issueSeverity = (from u in Users where u.Building.Country == c from i in u.Issues where i.Date > last30Days select i.Severity).Max() select new { Country = c, IssueSeverity = issueSeverity } 

当然,这是一个简化的例子。 但是,要点是我需要捕获一个日期并在其上过滤子查询。 我还需要根据父对象以不同方式过滤子查询。

我(基本上)尝试创建以下函数:

 public IQueryable FindSeverity(Expression<Func> predicate) { var last30Days = DateTime.Today.AddDays(-30); return from u in Users.Where(predicate) from i in u.Issues where i.Date > last30Days select i.Severity; } 

使用方法如下:

 from c in Countries let issueSeverity = FindSeverity(u => u.Building.Country == c).Max() select new { Country = c, IssueSeverity = issueSeverity } 

这会编译,但在运行时不起作用。 entity framework抱怨FindSeverity函数未知。

我尝试了几种不同的表现体操方法,但无济于事。

构建可重用的Entity Framework查询需要做什么?

我已经玩了一些你的问题,但没有最终令人满意的结果。 我只列出我能找到并理解的几点。

1)

我重写了你的最后一个代码片段(简化forms,没有投影到匿名类型)…

 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()Expression>主体 (在这种情况下Tint )。 (我不确定“身体”是否是正确的终点技术,但你知道我的意思:正确的部分来自Lambda箭头=>)。 当整个查询被翻译成表达式树时,该主体被翻译为对函数FindSeverity的方法调用。 (当您查看queryExpression属性时,您可以在调试器中看到这一点: FindSeverity直接是表达式树中的一个节点,而不是此方法的主体。)执行失败,因为LINQ to Entities不知道此方法。 在这种lambda表达式的主体中,您只能使用已知函数,例如静态System.Data.Objects.EntityFunctions类中的规范函数。

2)

构建查询的可重用部分的一种可能的一般方法是编写IQueryable自定义扩展方法,例如:

 public static class MyExtensions { public static IQueryable FindSeverity(this IQueryable query, Expression> 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 } 

…属于LINQ to Entities不支持的查询内部的“引用非标量变量”类别。 (在它工作的LINQ to Objects中。)上面查询中的非标量变量是Users 。 如果Building表不为空,则需要例外: “无法创建EntityType类型的常量值。在此上下文中仅支持基本类型(例如Int32,String和Guid’)。”

看起来您在数据库中的UserBuilding之间存在一对多关系,但此关联未在您的实体中完全建模: User具有导航属性BuildingBuilding没有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 } 

这不会创建引用非标量变量的上述exception。 但也许我误解了你的模型。