我有这个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
因为我在一个WCF Ria LinqToEntitiesDomainService中使用EF的项目上工作,我必须用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客户端。这也是为什么我需要为它设置一个"testd",因为它是一个实体,它需要一个。
问题是,这个linq查询产生了这个可怕的SQL语句:http://pastebay.com/144532
我想有更好的方法来查询这些信息,也许是一个更好的linq表达式。还是可以用普通的SQL查询?
编辑:我意识到testd是不必要的,因为另一个名为"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
这似乎比前一个半生不熟的linq查询的sql语句要好得多。但这就足够好了吗?或者这是LinqToEntities能力的边界,如果我想要更清晰的sql,我应该做另一个DomainService与LinqToSQL或其他东西?
或者最好的方法是使用存储过程,返回行集?如果是这样,是否有最佳实践来异步完成此操作,如简单的WCF Ria DomainService查询?我也想知道最好的做法。
编译lambda表达式linq可能会花费很多时间(3 - 30秒),特别是使用group by
和FirstOrDefault
(对于左内连接意味着只取组中第一行的值)。
生成的sql执行可能没有那么糟糕,但是使用。net 4.0无法预编译的DbContext进行编译。
例如:
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的编译时间,而在编译而不是加载数据(这需要少于500ms)的情况下,这些时间加起来是3倍= 6s。这基本上破坏了应用程序的可用性。用户将无缘无故地等待多次。
到目前为止,我们发现加快编译速度的唯一方法是将lambda表达式与对象表达式混合使用(可能不是正确的表法)。
示例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工作。