实体框架 - 如何避免查询重新编译?

Red*_*Red 5 c# entity-framework azure entity-framework-6 azure-sql-database

我正在致力于优化我们的实体框架代码,目前我面临着一个我不确定如何解决的问题。

我们使用Azure SQL + Code-First Entity Framework 6.1.3 + Asp.net Web Api v2。端点托管在云中,我使用它进行测试。

我有一个 API 操作,用于获取过滤、排序和分页数据。这是我处理数据的简化代码:

var entities = DbContext.Services
        .Include(q => q.Consumer.Building.Address.Country)
        .Include(q => q.Consumer.Building.Address.State);

entities = entities.OrderBy(x => x.Consumer.RegisteredAt);

entities = entities.Where(x => x.IsDeleted == false); 
entities = entities.Where(x => userId.HasValue ? x.Owner.Id == userId ? true); //this part comes from deep internals, so I cannot change it quickly
var page = entities = entities.Skip(skip).Take(take).ToList();
var count = entities.Count();

var dtoPage = Mapper.Map<IEnumerable<ServiceDto>>(page);

return Page<ServiceDto>(dtoPage, count);
Run Code Online (Sandbox Code Playgroud)

所以代码没有做任何特殊的事情——不使用IN子句等,只是简单的过滤、排序和分页。

问题: 调用此方法的执行时间不稳定。我使用一个简单的脚本来调用 API 端点,该端点使用不同的参数调用此代码:它一次又一次地获取页面 0..5 200 次。对于每个调用(第一个调用除外),我预计调用时间不会超过 300 毫秒。但是:从 1200 个总体调用中,有 36 个调用时间超过 1 秒 - 就像查询在这段时间内再次重新编译一样。平均调用时间为250ms,但有时会飙升至 1000ms ,相差 4 倍。

测试的常见行为是:

  1. 第一次查询很慢

  2. 第二个查询更快,但仍然比其余查询慢

  3. 除了时不时出现一些峰值之外,其余查询非常稳定且快速。

除了我之外没有人使用测试环境,所以这不是负载问题,我可以在任何环境上重现它,甚至在快速的本地 PC - i5 + 8gb RAM + SSD 上。

测试之间甚至有很小的延迟 -

简而言之,我的问题是: 这个问题来自哪里?

  1. 这真的是重新编译问题吗?如果不是,问题的可能根源是什么?
  2. 是否有关于实体框架 6 查询缓存失效的文档?
  3. 有没有办法让查询在缓存中保存更长时间,这样如果我现在和 30 分钟后查询该方法,就不必重新编译它?

jbl*_*jbl 4

这真的是重新编译的问题吗?

我认为这不是重新编译的问题。但是您可以通过Skip在 和Take调用中传递 lambda 来减少重新编译。请参阅4.2 使用使用常量生成查询的函数

如果不是,问题的可能根源是什么?

AFAIK SqlAzure 是共享环境。这可能是您的问题的原因。

是否有关于 Entity Framework 6 查询缓存失效的文档?有没有办法让查询在缓存中保存更长时间,这样如果我现在和 30 分钟后查询该方法,就不必重新编译它?

只要 AppDomain 存在,您的实体框架查询就会保留在内存中;只要服务器不需要内存,sql 查询计划就会保留在 Sql Server 内存中。与lambdaSkip一起Take也将有助于解决这个问题。请参阅同一页:

"In EF6 these methods have a lambda overload that effectively makes the
cached query plan reusable because EF can capture variables passed to
these methods and translate them to SQLparameters. This also helps
keep the cache cleaner since otherwise each query with a different
constant for Skip and Take would get its own query plan cache entry"
Run Code Online (Sandbox Code Playgroud)