Рефакторинг LINQ to SQL / Entities Query со многими подзапросами - PullRequest
5 голосов
/ 12 марта 2012

У меня есть следующий запрос 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 ]

2 голосов
/ 12 марта 2012

Я просто могу предложить уменьшить его длину примерно так (используя ключевое слово 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 */                              
          };

Это все еще один вызов в БД.

0 голосов
/ 12 марта 2012

Возможно, вы захотите использовать ключевое слово 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()

Черт, вы могли быдаже просто оставьте значение let во втором примере и просто используйте summaries сущности в ваших последних выборках.Возможно, вам придется немного поэкспериментировать с этим, чтобы убедиться, что вы не получите никакого дублирования значений, но, по крайней мере, таким образом, вы можете просто сделать прямой выбор против summaries вместо того, чтобы каждый раз переписывать свои подзапросы.

0 голосов
/ 12 марта 2012

На самом деле нет проблем в разделении вашего запроса на несколько методов.Однако есть некоторые условия.

Убедитесь, что ваш запрос 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)
}

Вы можете видеть, что запрос выполняется двумя способами.Все еще есть только один вызов в БД, потому что IEnumerable хранит запрос, а не результат.Запрос выполняется только при необходимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...