我有以下LINQ to Entities查询,它有许多子查询来获取一些聚合数据:
var systems = from s in db.Systems
orderby s.Name
select new SystemSummary
{
Id = s.Id,
Code = s.Code,
Name = s.Name,
LastException = (
from a in s.Applications
from e in a.Summaries
select e.CreationDate
).Max(),
TodaysExceptions = (
from a in s.Applications
from e in a.Summaries
where e.CreationDate >= today && e.CreationDate < tomorrow
select e
).Count(),
/* SNIP - 10-15 more subqueries */
};
我将查询缩短为仅包含2个子查询,但可能会有大约10-15个子查询。有没有办法可以重构查询来清理代码?我不是在寻求性能提升。我想通过将子查询放入单独的方法来清理代码,同时仍然确保它是对数据库的单个调用。这可能吗?
答案 0 :(得分:2)
我可以通过这样的方式最小化其长度(在原始查询中使用let
关键字):
var subQuery = from a in s.Applications
from e in a.Summaries
select e;
你也可以有一些重构:
subQuery.Count(e=>e.CreationDate >= today && e.CreationDate < tomorrow);
subQuery.max(e=>e.CreationDate);
实际上使用点表示法并将查询移动到相关函数而不是额外的where
子句。
并在您的查询中使用subQuery
:
from s in db.Systems
orderby s.Name
let subQuery = from a in s.Applications
from e in a.Summaries
select e
select new SystemSummary
{
Id = s.Id,
Code = s.Code,
Name = s.Name,
LastException = subQuery.max(e=>e.CreationDate),
TodaysExceptions = subQuery.Count(e=>e.CreationDate >= today
&& e.CreationDate < tomorrow),
/* SNIP - 10-15 more subqueries */
};
这仍然是对db的单次调用。
答案 1 :(得分:0)
将查询分成多个方法确实没有问题。但是有一些条件。
确保您的查询是IEumerable。这是默认情况。
IEnumerable可确保查询存储在变量中,但不会执行。 编译器在运行时优化您的查询。
快速而肮脏的例子:
private MyContext context = new MyContext()
private IEnumerable<User> getUser(Guid userID)
{
return context.User.Where(c => c.ID == userID);
}
private void evaluateUser()
{
bool isUserActive getUser().Any(c => c.IsActive)
}
您可以看到查询有两种方法。仍然只有一次调用DB,因为IEnumerable存储查询而不是结果。查询仅在需要时执行。
答案 2 :(得分:0)
您可能需要考虑使用let
关键字来创建查询本地的“变量”(这最终会成为您的子查询)。例如:
var systems = from s in db.Systems
orderby s.Name
let lastException = (from a in s.Applications from e in a.Summaries select e.CreationDate).Max()
...
你可以做的另一个选择是可以立即从各种协会创建一个子查询,并使用这些元素。
var systems = from s in db.Systems
orderby s.Name
from summaries in
(from ta in s.Applications
from te in ta.Summaries
...
select { APPS = ta, SUMMS = te ,/*anything else you want*/ })
let lastExpire = (from summaries select SUMMS.CreationDate).Max()
天啊,你甚至可以在第二个例子中留下让出来,只需在最终选择中使用summaries
个实体。您可能需要稍微调整一下以确保不会出现任何重复值,但至少这样您只需对summaries
进行直接选择,而不是每次都重写子查询。