如何检查 System.Runtime.Caching.ObjectCache 中的缓存策略?
Posted
技术标签:
【中文标题】如何检查 System.Runtime.Caching.ObjectCache 中的缓存策略?【英文标题】:How to inspect cache policies inside System.Runtime.Caching.ObjectCache? 【发布时间】:2011-06-23 18:54:31 【问题描述】:我正在使用新的 .NET 4.0 缓存命名空间:System.Runtime.Caching
。
现在,我只是对新 API 做一些原型/摆弄,以便找出最适合实际应用的方法。
与此一致,我正在尝试创建一个页面(ASP.NET MVC),它基本上会转储缓存中的所有内容,尤其是以下信息:
缓存键 缓存对象 缓存政策(到期日期等) 缓存依赖项(如果有)但是,除了钥匙/对象,我似乎什么都得不到。
这是我目前正在使用的代码:
public ActionResult Index()
var cache = MemoryCache.Default;
// i can get the list of cache keys like this:
var cacheKeys = cache.Select(kvp => kvp.Key).ToList();
// i can also get a strongly-typed "CacheItem" like this:
CacheItem item = cache.GetCacheItem("someKey");
我希望“CacheItem”类能够公开我需要的信息(到期、依赖关系等 - 至少作为“getter”)。
但事实并非如此。它只有键、值和区域名称的属性。
我如何检查缓存中的项目并吐出我需要的信息?
我是否缺少命名空间/类?
编辑
看起来有一个 ChangeMonitor 类,但同样 - 这不提供过期信息,它只是允许您在缓存项被删除时订阅事件。
必须有一种方法可以只抓取缓存中的项目,以及它们何时过期。
编辑 2
不知道这是否应该是一个单独的问题,而且 - 我对我应该给我的 ObjectCache 的生命周期感到困惑。 MSDN 说它不是单例,实际上您可以创建多个 ObjectCache 实例。但这意味着什么,我在访问 ObjectCache 实例时必须使用完全锁定的单例?
【问题讨论】:
【参考方案1】:在我看来,一旦将 CacheItemPolicy 添加到缓存集合中,就没有办法检索它。
可以想到的最好的解决方法是将策略对象与要缓存的项目一起缓存,但只需将“策略”附加到键名,以便稍后检索策略。这显然假设您首先可以控制实际将项目添加到缓存中。示例如下:
public ActionResult Index()
string key = "Hello";
string value = "World";
var cache = MemoryCache.Default;
CacheItemPolicy policy = new CacheItemPolicy();
policy.AbsoluteExpiration = DateTime.Now.AddDays(1);
cache.Add(new CacheItem(key, value), policy);
cache.Add(new CacheItem(key + "Policy", policy), null);
CacheItem item = cache.GetCacheItem(key);
CacheItem policyItem = cache.GetCacheItem(key + "Policy");
CacheItemPolicy policy2 = policyItem.Value as CacheItemPolicy;
ViewBag.Message = key + " " + value;
return View();
【讨论】:
是的,我想到了这个。也许只是为了原型设计,我会这样做 - 然后在我完成后将代码改回正常。尽管如此,我还是希望在实时服务器上有一个轻量级的缓存监控程序——听起来这不可能。【参考方案2】:我最近不得不为 MemoryCache
包装器创建单元测试。我想检查包装器是否使用预期的策略添加到底层缓存。
我想出了以下GetCacheItemPolicy
扩展方法,它大量使用反射。尽管我的目的只是对我的包装器进行单元测试,但我尝试优化每个反射,以便它可以在性能很重要的其他环境中使用(即使如果你在生产代码中这样做,很可能性能并不重要,但是嘿...)。
internal static class MemoryCacheExtensions
static MemoryCacheExtensions()
const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
Expression<Func<Func<object, object>>> createGetterExpression = () => CreateObjectGetter<object, object>(null);
var genericCreateObjectGetterMethodInfo = ((MethodCallExpression)createGetterExpression.Body).Method.GetGenericMethodDefinition();
// Basic types
var memoryCacheType = typeof(MemoryCache);
var getEntryMethodInfo = memoryCacheType.GetMethod("GetEntry", flags);
// MemoryCacheEntry fields
Debug.Assert(getEntryMethodInfo != null, nameof(getEntryMethodInfo) + " != null");
var memoryCacheEntryType = getEntryMethodInfo.ReturnType;
var valueFieldInfo = memoryCacheEntryType.GetField("_value", flags);
var usageBucketFieldInfo = memoryCacheEntryType.GetField("_usageBucket", flags);
var removedCallbackFieldInfo = memoryCacheEntryType.GetField("_callback", flags);
var seldomUsedFieldsFieldInfo = memoryCacheEntryType.GetField("_fields", flags);
var slidingExpirationFieldInfo = memoryCacheEntryType.GetField("_slidingExp", flags);
var absoluteExpirationFieldInfo = memoryCacheEntryType.GetField("_utcAbsExp", flags);
// SeldomUsedFields fields
Debug.Assert(seldomUsedFieldsFieldInfo != null, nameof(seldomUsedFieldsFieldInfo) + " != null");
var seldomUsedFieldsType = seldomUsedFieldsFieldInfo.FieldType;
var dependenciesFieldInfo = seldomUsedFieldsType.GetField("_dependencies", flags);
// SentinelEntry fields
var sentinelEntryType = memoryCacheType.GetNestedType("SentinelEntry", flags);
var updateCallbackFieldInfo = sentinelEntryType.GetField("_updateCallback", flags);
GetMemoryCacheEntry = (Func<MemoryCache, string, object>)Delegate
.CreateDelegate(typeof(Func<MemoryCache, string, object>), getEntryMethodInfo);
Debug.Assert(valueFieldInfo != null, nameof(valueFieldInfo) + " != null");
GetMemoryCacheEntryValue = (Func<object, object>)genericCreateObjectGetterMethodInfo
.MakeGenericMethod(memoryCacheEntryType, valueFieldInfo.FieldType)
.Invoke(null, new object[] valueFieldInfo );
Debug.Assert(usageBucketFieldInfo != null, nameof(usageBucketFieldInfo) + " != null");
GetMemoryCacheEntryUsageBucket = (Func<object, byte>)genericCreateObjectGetterMethodInfo
.MakeGenericMethod(memoryCacheEntryType, usageBucketFieldInfo.FieldType)
.Invoke(null, new object[] usageBucketFieldInfo );
Debug.Assert(removedCallbackFieldInfo != null, nameof(removedCallbackFieldInfo) + " != null");
GetMemoryCacheEntryRemovedCallback = (Func<object, CacheEntryRemovedCallback>)genericCreateObjectGetterMethodInfo
.MakeGenericMethod(memoryCacheEntryType, removedCallbackFieldInfo.FieldType)
.Invoke(null, new object[] removedCallbackFieldInfo );
GetMemoryCacheEntrySeldomUsedFields = (Func<object, object>)genericCreateObjectGetterMethodInfo
.MakeGenericMethod(memoryCacheEntryType, seldomUsedFieldsFieldInfo.FieldType)
.Invoke(null, new object[] seldomUsedFieldsFieldInfo );
Debug.Assert(slidingExpirationFieldInfo != null, nameof(slidingExpirationFieldInfo) + " != null");
GetMemoryCacheEntrySlidingExpiration = (Func<object, TimeSpan>)genericCreateObjectGetterMethodInfo
.MakeGenericMethod(memoryCacheEntryType, slidingExpirationFieldInfo.FieldType)
.Invoke(null, new object[] slidingExpirationFieldInfo );
Debug.Assert(absoluteExpirationFieldInfo != null, nameof(absoluteExpirationFieldInfo) + " != null");
GetMemoryCacheEntryAbsoluteExpiration = (Func<object, DateTime>)genericCreateObjectGetterMethodInfo
.MakeGenericMethod(memoryCacheEntryType, absoluteExpirationFieldInfo.FieldType)
.Invoke(null, new object[] absoluteExpirationFieldInfo );
Debug.Assert(dependenciesFieldInfo != null, nameof(dependenciesFieldInfo) + " != null");
GetSeldomUsedFieldsDependencies = (Func<object, Collection<ChangeMonitor>>)genericCreateObjectGetterMethodInfo
.MakeGenericMethod(seldomUsedFieldsType, dependenciesFieldInfo.FieldType)
.Invoke(null, new object[] dependenciesFieldInfo );
Debug.Assert(updateCallbackFieldInfo != null, nameof(updateCallbackFieldInfo) + " != null");
GetSentinelEntryUpdateCallback = (Func<object, CacheEntryUpdateCallback>)genericCreateObjectGetterMethodInfo
.MakeGenericMethod(sentinelEntryType, updateCallbackFieldInfo.FieldType)
.Invoke(null, new object[] updateCallbackFieldInfo );
private static Func<MemoryCache, string, object> GetMemoryCacheEntry get;
private static Func<object, object> GetMemoryCacheEntryValue get;
private static Func<object, byte> GetMemoryCacheEntryUsageBucket get;
private static Func<object, CacheEntryRemovedCallback> GetMemoryCacheEntryRemovedCallback get;
private static Func<object, object> GetMemoryCacheEntrySeldomUsedFields get;
private static Func<object, TimeSpan> GetMemoryCacheEntrySlidingExpiration get;
private static Func<object, DateTime> GetMemoryCacheEntryAbsoluteExpiration get;
private static Func<object, Collection<ChangeMonitor>> GetSeldomUsedFieldsDependencies get;
private static Func<object, CacheEntryUpdateCallback> GetSentinelEntryUpdateCallback get;
public static CacheItemPolicy GetCacheItemPolicy(this MemoryCache memoryCache, string key)
var entry = GetMemoryCacheEntry(memoryCache, key);
if (entry == null) return null;
var sentinel = GetMemoryCacheEntry(memoryCache, "OnUpdateSentinel" + key);
var sentinelValue = sentinel == null ? null : GetMemoryCacheEntryValue(sentinel);
var usageBucket = GetMemoryCacheEntryUsageBucket(entry);
var slidingExpiration = GetMemoryCacheEntrySlidingExpiration(entry);
var absoluteExpiration = GetMemoryCacheEntryAbsoluteExpiration(entry);
var seldomUsedFields = GetMemoryCacheEntrySeldomUsedFields(entry);
var changeMonitors = seldomUsedFields == null ? null : GetSeldomUsedFieldsDependencies(seldomUsedFields);
var removedCallback = GetMemoryCacheEntryRemovedCallback(entry);
var updatedCallback = sentinelValue == null ? null : GetSentinelEntryUpdateCallback(sentinelValue);
var cacheItemPolicy = new CacheItemPolicy
Priority = usageBucket == 0xFF ? CacheItemPriority.NotRemovable : CacheItemPriority.Default,
AbsoluteExpiration = absoluteExpiration,
RemovedCallback = removedCallback,
SlidingExpiration = slidingExpiration,
UpdateCallback = updatedCallback,
;
if (changeMonitors != null)
foreach (var changeMonitor in changeMonitors)
cacheItemPolicy.ChangeMonitors.Add(changeMonitor);
return cacheItemPolicy;
private static Func<TTarget, TFieldType> CreateGetter<TTarget, TFieldType>(FieldInfo field)
var fieldReflectedType = field.ReflectedType;
Debug.Assert(fieldReflectedType != null, "field.ReflectedType != null");
var methodName = fieldReflectedType.FullName + ".get_" + field.Name;
var getterMethod = new DynamicMethod(methodName, typeof(TFieldType), new[] typeof(TTarget) , true);
var generator = getterMethod.GetILGenerator();
if (field.IsStatic)
generator.Emit(OpCodes.Ldsfld, field);
else
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, field);
generator.Emit(OpCodes.Ret);
return (Func<TTarget, TFieldType>)getterMethod.CreateDelegate(typeof(Func<TTarget, TFieldType>));
private static Func<object, TFieldType> CreateObjectGetter<TTarget, TFieldType>(FieldInfo fieldInfo)
var getter = CreateGetter<TTarget, TFieldType>(fieldInfo);
return @object => getter((TTarget)@object);
【讨论】:
您错过了提高反射性能的最简单方法之一,即通过缓存(例如存储在静态属性中)反射获得的 xxxInfo(如 FieldInfo),并重新使用它们以上是关于如何检查 System.Runtime.Caching.ObjectCache 中的缓存策略?的主要内容,如果未能解决你的问题,请参考以下文章