使用许多子查询重构LINQ to SQL / Entities查询

时间:2012-03-12 16:48:27

标签: c# entity-framework linq-to-sql linq-to-entities

我有以下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个子查询。有没有办法可以重构查询来清理代码?我不是在寻求性能提升。我想通过将子查询放入单独的方法来清理代码,同时仍然确保它是对数据库的单个调用。这可能吗?

3 个答案:

答案 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进行直接选择,而不是每次都重写子查询。