Reputation: 1699
I am using the .NET 4.0 MemoryCache
class, and I want to add or replace an existing item in the cache in a thread-safe manner, but I also want to know whether I have replaced an existing item or added a new one.
From what I can tell, the Set
method is intended for the purpose of atomically replacing an item in the cache if it exists, whereas the AddOrGetExisting
method will atomically get me the existing item without replacing it.
However, the Set method doesn't return the item that was replaced, it just replaces it. It is possible to hook up a removed event to the CacheItemPolicy
for items, but hard to match that up with the new item that replaced it.
I can approximate it with the following method, but it seems a bit heavy (because of asking the cache to essentially add an item twice) and I wanted to ask if someone knows an easier way to do it.
public object SetAndGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null)
{
lock (_cacheLock)
{
var existing = _cache.AddOrGetExisting(key, value, policy, regionName);
if (existing != null)
{
_cache.Set(key, value, policy, regionName);
return existing;
}
return null;
}
}
Upvotes: 3
Views: 3246
Reputation: 3397
After thinking about it, in your example, you are updating it regardless, so AddOrGetExisting
isn't the best, as you mentioned, you are setting it later anyway. Below it a simple implementation that I think fits what you are asking for. If I am wrong, please let me know!
public class MemoryCacheWithEvents
{
private static MemoryCache _cache = new MemoryCache("myCache");
private static object _syncLock = new object();
public EventHandler<CacheChangeEventArgs> AddingCacheItem;
public EventHandler<CacheChangeEventArgs> UpdatingCacheItem;
public object GetAndSetExisting(string key, object value, CacheItemPolicy policy, string regionName = null)
{
lock (_syncLock)
{
var cacheItem = new CacheItem(key, value, regionName);
var existing = _cache.GetCacheItem(key, null);
if (existing == null)
{
OnAddingCacheItem(new CacheChangeEventArgs(null, cacheItem));
}
else
{
OnUpdatingCacheItem(new CacheChangeEventArgs(existing, cacheItem));
}
_cache.Set(cacheItem, policy);
return existing;
}
}
public virtual void OnAddingCacheItem(CacheChangeEventArgs eventArgs){
var handler = AddingCacheItem;
if (handler != null)
{
handler(this, eventArgs);
}
}
public virtual void OnUpdatingCacheItem(CacheChangeEventArgs eventArgs){
var handler = UpdatingCacheItem;
if (handler != null)
{
handler(this, eventArgs);
}
}
}
public class CacheChangeEventArgs : EventArgs
{
public object OldCacheItem { get; set; }
public object NewCacheItem { get; set; }
public CacheChangeEventArgs(object oldCacheItem, object newCacheItem)
{
this.OldCacheItem = oldCacheItem;
this.NewCacheItem = newCacheItem;
}
}
Upvotes: 1