链接到已编译的查询会失去性能优势

时间:2011-11-08 07:06:20

标签: linq entity-framework linq-to-entities

我开始使用编译查询来提高一些常用的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;

3 个答案:

答案 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)