Joh*_*zen 6 .net c# linq-to-entities entity-framework composition
我的应用程序中有一些(相当多余的)查询,如下所示:
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
}
Run Code Online (Sandbox Code Playgroud)
和:
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
}
Run Code Online (Sandbox Code Playgroud)
当然,这是一个简化的例子.但是,要点是我需要捕获日期并在其上过滤子查询.我还需要根据父对象以不同方式过滤子查询.
我尝试(基本上)创建以下函数:
public IQueryable<int?> FindSeverity(Expression<Func<User, bool>> 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;
}
Run Code Online (Sandbox Code Playgroud)
使用方法如下:
from c in Countries
let issueSeverity = FindSeverity(u => u.Building.Country == c).Max()
select new
{
Country = c,
IssueSeverity = issueSeverity
}
Run Code Online (Sandbox Code Playgroud)
这会编译,但在运行时不起作用.实体框架抱怨FindSeverity功能未知.
我尝试了几种不同的表现体操方法,但无济于事.
构建可重用的Entity Framework查询需要做什么?
我已经解决了你的问题,但没有最终令人满意的结果。我只列出我能找到并理解的几点。
1)
我重写了你的最后一个代码片段(以简化的形式,没有投影到匿名类型)...
var query = from c in Countries
select FindSeverity(u => u.Building.Country == c).Max();
Run Code Online (Sandbox Code Playgroud)
...然后在扩展方法语法中:
var query = Countries
.Select(c => FindSeverity(u => u.Building.Country == c).Max());
Run Code Online (Sandbox Code Playgroud)
现在我们可以更好地看到(在本例中是FindSeverity(u => u.Building.Country == c).Max())的主体。(我不确定“body”是否是正确的技术终点,但你知道我的意思:Lambda 箭头右侧的部分=>)。当整个查询被转换为表达式树时,该主体被转换为对函数的方法调用。(当您观察:的属性直接是表达式树中的一个节点,而不是此方法的主体时,您可以在调试器中看到这一点。)执行时会失败,因为 LINQ to Entities 不知道此方法。在此类 lambda 表达式的主体中,您只能使用已知函数,例如静态类中的规范函数。Expression<Func<Country, T>>TintFindSeverityExpressionqueryFindSeveritySystem.Data.Objects.EntityFunctions
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;
}
}
Run Code Online (Sandbox Code Playgroud)
然后你可以编写如下查询:
var max1 = Users.FindSeverity(u => u.Building.ID == 1).Max();
var max2 = Users.FindSeverity(u => u.Building.Country == "Wonderland").Max();
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,您被迫使用扩展方法语法编写查询。我没有看到在查询语法中使用此类自定义查询扩展方法的方法。
上面的示例只是创建可重用查询片段的通用模式,但它对于您问题中的特定查询并没有真正的帮助。至少我不知道如何重新制定你的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
}
Run Code Online (Sandbox Code Playgroud)
...属于查询内的“引用非标量变量”类别,LINQ to Entities 不支持该类别。(在 LINQ to Objects 中它可以工作。)上面查询中的非标量变量是Users。如果Building表不为空,则会出现异常:“无法创建 EntityType 类型的常量值。此上下文中仅支持原始类型(‘例如 Int32、String 和 Guid’)。”
看起来您在数据库中User和 之间具有一对多关系,但此关联并未在您的实体中完全建模:具有导航属性,但没有. 在这种情况下,我希望查询中有一个,如下所示:BuildingUserBuildingBuildingUsersJoin
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
}
Run Code Online (Sandbox Code Playgroud)
这不会创建提到的引用非标量变量的异常。但也许我误解了你的模型。
| 归档时间: |
|
| 查看次数: |
1884 次 |
| 最近记录: |