简体   繁体   English

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

[英]Refactor LINQ to SQL/Entities Query with many subqueries

I have the following LINQ to Entities query which has many subqueries to get some aggregate data: 我有以下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 */                              
              };

I shortened up the query to only include 2 of the subqueries, but there could be around 10-15 more of them. 我将查询缩短为仅包含2个子查询,但可能会有大约10-15个子查询。 Is there a way that I can refactor the query to clean up the code? 有没有办法可以重构查询来清理代码? I'm not looking for a performance increase. 我不是在寻求性能提升。 I want to just clean up the code by maybe putting the subqueries into separate methods while still making sure that it is a single call to the database. 我想通过将子查询放入单独的方法来清理代码, 同时仍然确保它是对数据库的单个调用。 Is this possible? 这可能吗?

I just can offer minimize its length by something like this (by using let keyword in your original query): 我可以通过这样的方式提供最小化其长度(在原始查询中使用let关键字):

var subQuery =    from a in s.Applications
                  from e in a.Summaries
                  select e;

Also you can have some refactors like: 你也可以有一些重构:

subQuery.Count(e=>e.CreationDate >= today && e.CreationDate < tomorrow);

subQuery.max(e=>e.CreationDate);

In fact use dot notation and move your query to related function instead of extra where clause. 实际上使用点表示法并将查询移动到相关函数而不是额外的where子句。

and use subQuery in your query: 并在您的查询中使用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 */                              
          };

This is still single call to db. 这仍然是对db的单一调用。

There really is no problem in seperating your query into multiple methods. 将查询分成多个方法确实没有问题。 There are some conditions though. 但是有一些条件。

Make sure that your query is IEumerable. 确保您的查询是IEumerable。 This is by default. 这是默认情况。

IEnumerable ensures that the query is stored in the variable, but not executed. IEnumerable确保查询存储在变量中,但不执行。 The compiler optimizes your queries at run time. 编译器在运行时优化您的查询。

Quick and dirty exemple: 快速而肮脏的例子:

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)
}

You can see that the query is in two methods. 您可以看到查询有两种方法。 There is still only one call to the DB because an IEnumerable stores the query and not the result. 仍然只有一次调用DB,因为IEnumerable存储查询而不是结果。 The query is executed only when needed. 仅在需要时执行查询。

You may want to consider using the let keyword to create "variables" local to the query (this ultimately ends up being your subqueries). 您可能需要考虑使用let关键字来创建查询本地的“变量”(这最终会成为您的子查询)。 For example: 例如:

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()
              ...

Another option you could do is possibly create a subquery from the various associations right off the bat, and work with those elements. 您可以做的另一个选择是可以立即从各种关联创建一个子查询,并使用这些元素。

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()

Hell, you could even just leave the let out in the second example and just use the summaries entities in your final selects. 地狱,你甚至可以在第二个例子中留下让出来,只需在你的最终选择中使用summaries实体。 You might have to play around with it a bit to make sure you don't get any duplication of values, but at least this way you can just do a straight select against your summaries instead of rewriting your subqueries each time. 您可能需要稍微调整一下以确保不会出现任何重复值,但至少这样您只需对summaries进行直接选择,而不是每次都重写子查询。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM