何时应该使用CompiledQuery?

23

我有一张表格:

-- Tag

ID  | Name
-----------
1   | c#
2   | linq
3   | entity-framework

我有一个类,将具有以下方法:

IEnumerable<Tag> GetAll();
IEnumerable<Tag> GetByName();

在这种情况下,我应该使用已编译的查询吗?
static readonly Func<Entities, IEnumerable<Tag>> AllTags =
    CompiledQuery.Compile<Entities, IEnumerable<Tag>>
    (
        e => e.Tags
    );

那么我的GetByName方法将会是:

IEnumerable<Tag> GetByName(string name)
{
    using (var db = new Entities())
    {
        return AllTags(db).Where(t => t.Name.Contains(name)).ToList();
    }
}

这个代码生成一个 SELECT ID, Name FROM Tag,并在代码中执行 Where。在这种情况下,我应该避免使用 CompiledQuery 吗?

基本上,我想知道何时应使用编译查询。此外,在网站上,它们仅为整个应用程序编译一次吗?


你提供的示例在运行时会失败(你必须传递上下文,并且不能在没有Expression<>的情况下使用Func<>。此外,进一步组合查询(Where(t => ...)将“反编译”结果。但问题是有效的。 - Craig Stuntz
1
在 .net 4.5 中,这将自动完成。请参见“Entity Framework 5: Controlling automatic query compilation”(http://blogs.msdn.com/b/stuartleeks/archive/2012/06/12/entity-framework-5-controlling-automatic-query-compilation.aspx)。 - Michael Freidgeim
5个回答

33
当以下所有条件均成立时,应使用CompiledQuery
  • 查询将被执行多次,仅通过参数值的变化而有所不同。
  • 查询足够复杂,表达式计算和视图生成的成本“显著”(试错)。
  • 您未使用像IEnumerable<T>.Contains()这样的LINQ功能,因为它不能与CompiledQuery一起使用。
  • 在可能的情况下,您已经简化了查询,从而获得更大的性能优势。
  • 您不打算进一步组合查询结果(例如,限制或投影),这会产生“反编译”效果。

CompiledQuery在第一次执行查询时完成其工作。对于第一次执行,它不会提供任何好处。与任何性能调优一样,通常要避免使用它,直到确定正在修复实际性能热点。

2012更新:EF 5将自动执行此操作(请参见“Entity Framework 5:控制自动查询编译”)。因此,请将“您未使用EF 5”添加到上述列表中。


6
编译查询可以节省时间,因为不需要生成表达式树。如果该查询经常使用并且您保存了编译查询,则应该绝对使用它。我曾经遇到过很多情况,查询解析所花费的时间比实际往返数据库所花费的时间还要长。
在您的情况下,如果您确定它会生成SELECT ID, Name FROM Tag而没有WHERE语句(我怀疑,因为您的AllQueries函数应该返回IQueryable,实际查询应该只在调用ToList之后进行)- 您不应该使用它。
正如其他人已经提到的,在较大的表上执行SELECT * FROM [someBigTable]会花费很长时间,而您在客户端上进行过滤也会花费更多的时间。因此,无论您是否使用编译查询,都应该确保在数据库端进行过滤。

1

编译查询在使用大型表达式树的LINQ查询中更有帮助,例如复杂查询,可以通过重复使用查询而获得性能提升,而不是一遍又一遍地构建表达式树。在您的情况下,我想它只会节省非常少的时间。


在一个有100万行的表中,SELECT ID, Name FROM TagsSELECT ID, Name FROM Tags WHERE Name LIKE '%name%' 这两个查询语句,编译后执行第一个查询再在代码中过滤结果,是否能提高性能? - BrunoLM
不要在代码层面上过滤记录,而应该在数据库层面上进行过滤。在代码层面上过滤将是一个严重错误。编译查询只意味着表达式树已经预编译。第二个查询才是正确的方法。 - Mubashir Khan

1

编译查询是在应用程序编译时编译的,每次重复使用查询或者查询比较复杂时,您一定要尝试编译查询以加快执行速度。

但是我不会在所有查询中都使用它,因为这需要编写更多的代码,对于简单的查询可能不值得。

但是为了获得最佳性能,您还应该评估存储过程,在其中将所有处理工作都放在数据库服务器上,即使 Linq 尽可能地将大部分工作推送到数据库中,您仍会遇到存储过程更快的情况。


7
CompiledQuery不是在编译时编译的,而是在第一次执行之前动态编译。 - Craig Stuntz

0

编译查询可以提高性能,但并不是非常明显。如果您有复杂的查询,我建议尽可能使用存储过程或视图;让数据库自己来处理可能是更好的方法。


1
我们的测试表明,如果您重复使用查询,由于编译时间的原因,它可能会很快地累积到明显的延迟。特别是如果您有许多不同的查询。 - David Mårtensson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接