我在EF6中遇到LINQ查询编译速度慢的问题。我知道EF缓存LINQ查询的编译查询计划,但是有一些问题(例如Enumerable.Contains阻止缓存)。我想查看缓存以进行调试,以验证我是否正在为我的查询获得正确的缓存。我怎么能这样做?
注意:由于这纯粹是为了调试,我会对使用反射或其他不会在生产中使用的方法的答案感到满意。
答案 0 :(得分:4)
您可以使用类似的内容(在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
的方法创建一个类。当班级看到&#34;打开的连接时,它会启动一个计时器......&#34;消息,然后在看到&#34; - 执行...&#34;时停止计时器。那个时间大致对应于EF将LINQ查询编译为SQL所需的时间。如果您的查询正在被缓存,那么在第一次执行查询后,该时间将缩减为几乎没有;如果没有缓存,时间将保持一直很高。
(不用说,你只想在测试环境中这样做。)
答案 1 :(得分:2)
您可以注入RuntimeException
实现来记录任何查询树创建。我成功地将它与一个调用堆栈键控字典结合使用,用于发现多个“坏”查询的编译。