您可以使用类似这样的方式(在 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 所需的时间。如果您的查询正在被缓存,那么在第一次执行查询后,该时间将缩短到几乎为零;如果它没有被缓存,时间会一直保持很高。
(不用说您只想在测试环境中执行此操作。)