我开始使用编译查询来提高一些常用的linq到实体查询的性能。在一个场景中,我只将查询简化为最基本的形式并预先编译,然后我根据用户输入添加其他where子句。
在这种特殊情况下,我似乎失去了编译查询的性能优势。有人可以解释原因吗?
以下是我正在做的一个例子......
IEnumerable<Task> tasks = compiledQuery.Invoke(context, userId);
if(status != null)
{
tasks = tasks.Where(x=x.Status == status);
}
if(category != null)
{
tasks = tasks.Where(x=x.Category == category);
}
return tasks;
答案 0 :(得分:1)
(注意:我已经做了很多年龄段的EF工作,然后它只是喋喋不休。这只是一个明智的猜测,真的。)
这可能是罪魁祸首:
IEnumerable<Task> tasks = compiledQuery.Invoke(context, userId);
任何进一步的查询都必须在.NET进程中完成,而不是在SQL中完成。所有可能的结果都必须从数据库中获取并在本地过滤。试试这个:
IQueryable<Task> tasks = compiledQuery.Invoke(context, userId);
(当然,假设这是有效的。)
答案 1 :(得分:1)
我认为理解EF中的编译查询是如何工作的很重要。
执行查询时,实体框架将借助映射文件(EDMX或代码,首先是模型定义)将表达式树映射到SQL查询。这可能是一项复杂且性能密集的任务。
预编译存储这些映射阶段的结果,因此下次您点击查询时,它已经具有SQL,并且只需设置当前参数。
问题是,一旦修改了查询,预编译的查询就会失去它的性能优势。假设您有以下内容:
IQueryable query = GetCompiledQuery(); // => db.Tasks.Where(t => t.Id == myId);
var notModifiedResult = query.ToList(); // Fast
int ModifiedResult = query.Count(); // Slow
使用第一个查询,您将获得预编译的所有好处,因为EF已经为您生成了SQL,并且可以立即执行此操作。 第二个查询将丢失预编译,因为它必须重新生成它的SQL。
如果您现在要对notModifiedResult
执行查询,那么这将是一个Linq To Objects,因为您已经将SQL执行到数据库并获取了内存中的所有元素。
但是,您可以链接编译查询(即,在另一个编译查询中使用已编译的查询)。
但是你的代码需要一系列编译查询: - 默认值 - 其中status!= null - 一个类别!= null - 状态和类别都是一个!= null
答案 2 :(得分:0)
无法更改已编译的查询,只能更改参数。你在这里做的是实际运行查询,然后过滤结果。
.Invoke(context, userId); // returns all the results
.Where(....) // filters on that entire collection
您可以查看是否有一种聪明的方式来重新查询,以便参数可以包含在所有情况中,但不会产生任何影响。我没有使用编译的查询,对不起,但这是否有效(使用-1作为“忽略”值)?
// bunch of code to define the compiled query part, copied from [msdn][1]
(ctx, total) => from order in ctx.SalesOrderHeaders
where (total == -1 || order.TotalDue >= total)
select order);
在SQL中,您可以使用动态sql,或者传入的默认值(或null)来表示该参数应该被忽略
select * from table t
where
(@age = 0 or t.age = @age) and
(@weight is null or t.weight = @weight)