【问题标题】:How can I view the Entity Framework LINQ query plan cache?如何查看 Entity Framework LINQ 查询计划缓存?
【发布时间】:2016-01-11 18:31:46
【问题描述】:

我在 EF6 中遇到了慢速 LINQ 查询编译问题。我知道 EF 为 LINQ 查询缓存已编译的查询计划,但存在一些问题(例如 Enumerable.Contains 阻止缓存)。我想查看缓存以进行调试,以验证我是否为我的查询获得了正确的缓存。我该怎么做?

注意:因为这纯粹是为了调试,所以我很乐意使用反射或其他不会在生产中使用的方法来回答。

【问题讨论】:

  • 你的意思是 SQL server 缓存查询计划,而不是 EF?
  • @Magnus EF 缓存 LINQ 到 SQL 的转换。 SqlServer 将 SQL 的转换缓存为执行查询的“查询计划”。我对 EF 正在做什么很感兴趣。
  • 好的,所以您对查询本身感兴趣,而不是查询计划。

标签: entity-framework linq caching entity-framework-6


【解决方案1】:

您可以使用类似这样的方式(在 EF 6.1.3 中)反映到缓存:

var method = context.Database.GetType().GetMethod("CreateStoreItemCollection", BindingFlags.Instance | BindingFlags.NonPublic);
var storeItemsCollection = method.Invoke(context.Database, null);
var queryCacheManagerField = storeItemsCollection.GetType().GetField("_queryCacheManager", BindingFlags.Instance | BindingFlags.NonPublic);
var queryCacheManager = queryCacheManagerField.GetValue(storeItemsCollection);
var cacheField = queryCacheManager.GetType().GetField("_cacheData", BindingFlags.Instance | BindingFlags.NonPublic);
var cacheData = cacheField.GetValue(queryCacheManager) as ICollection;
foreach (var item in cacheData)
{
    Console.WriteLine(item.ToString());
}

不幸的是,缓存中的所有项目都是internal 类型(在System.Data.Entity.Core.Common.QueryCache 命名空间中),因此从它们中获取有用的信息需要更多的反思和探索。幸运的是,CompiledQueryCacheKey 覆盖了ToString,因此它放弃了一些关于自身的(神秘的)信息。运行单个查询 (Table.Count()) 后,上面的代码会输出以下两个条目:

[System.Data.Entity.Core.Common.QueryCache.ShaperFactoryQueryCacheKey`1[System.Int32], System.Data.Entity.Core.Common.QueryCache.QueryCacheEntry]

[FUNC<Edm.Count(In Transient.collection[Edm.Int32(Nullable=True,DefaultValue=)](Nullable=True,DefaultValue=))>:ARGS(([Project](BV'LQ1'=([Scan](DashboardAutoContext.Organizations:Transient.collection[DashboardAuto.Organization(Nullable=True,DefaultValue=)]))(1:Edm.Int32(Nullable=True,DefaultValue=)))))|||AppendOnly|True, System.Data.Entity.Core.Common.QueryCache.QueryCacheEntry]

祝你好运弄清楚这意味着什么,或者当你有一堆条目时将事情关联起来。

我取得了一些成功的另一种策略是创建一个类,该类具有我可以在测试期间分配给DbContext.Database.Log 的方法。该类在看到“打开的连接...”消息时启动计时器,然后在看到“-- Executing at ...”消息时停止计时器。该时间大致对应于 EF 将您的 LINQ 查询编译为 SQL 所需的时间。如果您的查询正在被缓存,那么在第一次执行查询后,该时间将缩短到几乎为零;如果它没有被缓存,时间会一直保持很高。

(不用说您只想在测试环境中执行此操作。)

【讨论】:

    【解决方案2】:

    您可以注入IDbCommandTreeInterceptor 实现来记录任何查询树的创建。我成功地将它与调用堆栈键控字典结合使用,以发现多个“坏”查询的编译。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-07-12
      • 1970-01-01
      • 1970-01-01
      • 2023-03-26
      • 2014-02-14
      • 2011-09-19
      • 1970-01-01
      相关资源
      最近更新 更多