Как упростить этот LINQ to Entities Query, чтобы сделать из него менее ужасный оператор SQL?(содержит Distinct, GroupBy и Count) - PullRequest
1 голос
/ 14 ноября 2011

У меня есть это выражение SQL:

SELECT Musclegroups.Name, COUNT(DISTINCT Workouts.WorkoutID) AS Expr1
FROM   Workouts INNER JOIN
       Series ON Workouts.WorkoutID = Series.WorkoutID INNER JOIN
       Exercises ON Series.ExerciseID = Exercises.ExerciseID INNER JOIN
       Musclegroups ON Musclegroups.MusclegroupID = Exercises.MusclegroupID
GROUP BY Musclegroups.Name

Так как я работаю над проектом, который использует EF в WCF Ria LinqToEntitiesDomainService, я должен запросить это с помощью LINQ (если это не является обязательным, пожалуйста, сообщите мне). Я сделал это выражение:

var WorkoutCountPerMusclegroup = (from s in ObjectContext.Series1
                                 join w in ObjectContext.Workouts on s.WorkoutID equals w.WorkoutID
                                 where w.UserID.Equals(userid) && w.Type.Equals("WeightLifting")
                                 group s by s.Exercise.Musclegroup into g                                              
                                 select new StringKeyIntValuePair
                                 {
                                      TestID = g.Select(n => n.Exercise.MusclegroupID).FirstOrDefault(),                                                  
                                      Key = g.Select(n => n.Exercise.Musclegroup.Name).FirstOrDefault(),
                                      Value = g.Select(n => n.WorkoutID).Distinct().Count()
                                 });

StringKeyIntValuePair - это просто пользовательский тип сущности, который я создал, чтобы я мог отправить информацию клиенту Silverlight. Кроме того, именно поэтому мне нужно установить для него «TestID», так как он является сущностью и ему нужен.

И проблема в том, что этот запрос linq производит ужасный оператор SQL: http://pastebay.com/144532

Полагаю, есть лучший способ запроса этой информации, возможно, лучшее выражение linq. Или можно просто как-нибудь запросить простой SQL?

EDIT:

Я понял, что TestID не нужен, потому что другое свойство с именем «Key» (то, по которому я группирую) становится ключом группы, поэтому он также будет ключом. И после этого мой запрос выглядит так:

var WorkoutCountPerMusclegroup = (from s in ObjectContext.Series1
                                          join w in ObjectContext.Workouts on s.WorkoutID equals w.WorkoutID
                                          where w.UserID.Equals(userid) && w.Type.Equals("WeightLifting")
                                          group w.WorkoutID by s.Exercise.Musclegroup.Name into g                                              
                                          select new StringKeyIntValuePair
                                          {                                                                                                   
                                              Key = g.Key,
                                              Value = g.Select(n => n).Distinct().Count()
                                          });

Это приводит к следующему SQL: http://pastebay.com/144545

Это выглядит намного лучше, чем предыдущая инструкция sql полуготового запроса linq. Но достаточно ли это хорошо? Или это граница возможностей LinqToEntities, и если я хочу еще более ясного sql, я должен сделать еще один DomainService, который работает с LinqToSQL или чем-то еще?

Или лучше всего использовать хранимую процедуру, которая возвращает наборы строк? Если да, то есть ли лучший способ сделать это асинхронно, например, простой запрос WCF Ria DomainService?

1 Ответ

1 голос
/ 29 августа 2012

Я также хотел бы ознакомиться с лучшими практиками.

Компиляция лямбда-выражения linq может занять много времени (3–30 с), особенно с использованием group by, а затем FirstOrDefault (для левой внутренней части).присоединяется, означая только получение значений из первой строки в группе).

Сгенерированное исключение sql может быть не таким уж плохим, но компиляция с использованием DbContext, который не может быть предварительно скомпилирован с .NET 4.0.

Asпример 1 примерно такой:

 var q = from a in DbContext.A
     join b ... into bb from b in bb.DefaultIfEmtpy()
     group new { a, b } by new { ... } into g
     select new 
     { 
        g.Key.Name1
        g.Sum(p => p.b.Salary)
        g.FirstOrDefault().b.SomeDate
     };

Каждый FirstOrDefault, который мы добавили в одном случае, вызывал + 2s время компиляции, которое складывалось 3 раза = 6 только для компиляции не загружаемых данных (что занимает менее 500 мс).Это в основном разрушает удобство использования вашего приложения.Пользователь будет ждать много раз без всякой причины.

Единственный способ ускорить компиляцию, который мы нашли до сих пор, - это смешивать лямбда-выражение с выражением объекта (возможно, это не правильная запись).

Пример 2: рефакторинг предыдущего примера 1.

var q = (from a in DbContext.A
     join b ... into bb from b in bb.DefaultIfEmtpy()
     select new { a, b })
     .GroupBy(p => new { ... })
     .Select(g => new 
        { 
           g.Key.Name1
           g.Sum(p => p.b.Salary)
           g.FirstOrDefault().b.SomeDate
        });

Вышеприведенный пример скомпилировал намного быстрее, чем пример 1 в нашем случае, но все еще не достаточно быстро, поэтому единственное решение для нас в критически важных областяхдолжен вернуться к собственному SQL (к сущностям) или использовать представления или хранимые процедуры (в нашем случае Oracle PL / SQL).

Как только у нас будет время, мы собираемся проверить, работает ли прекомпиляция в .NET 4.5 и /или .NET 5.0 для DbContext.

Надеюсь, это поможет, и мы сможем найти другие решения.

...