我已成功使用 EF Provider 包装器来执行“L2”缓存: https ://efwrappers.codeplex.com/
该设计支持多个“缓存提供程序”(内存中、asp.net 缓存)。现在我需要把它带到一个集群中,共享缓存。我发现 Julie Lerman 的一篇文章展示了如何使用 AppFabric 作为缓存提供程序:http: //msdn.microsoft.com/en-us/magazine/hh394143.aspx
该代码来自一篇 MSDN 文章,但它似乎是一个损坏的设计。我不是 AppFabric 方面的专家,但我看到了 3 个问题,其中一个是根本不正确地使用缓存 api。还有其他人在使用 AppFabric 缓存提供程序吗?其他人是否在 Julie 提供的代码示例中看到了问题?
//This class was based on the VelocityCacheAdapter clas
//MSDN Magazine Sept 2011 Data Points Column Sample Code
//(Julie Lerman)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using EFCachingProvider.Caching;
using Microsoft.ApplicationServer.Caching;
namespace EFAppFabricCacheAdapter
{
public class AppFabricCache : ICache
{
private DataCache _cache;
public AppFabricCache(DataCache cache)
{
_cache = cache;
}
public bool GetItem(string key, out object value)
{
key = GetCacheKey(key);
value = _cache.Get(key);
return value != null;
}
//!!!ISSUE #1: Minor issue: No try/catch around the call to _cache.Put.
// The event that separate workers try to update the same entry will cause
// exceptions.
//!!!ISSUE #2: The query is cached without a region.
// The dependent sets are cached in regions.
//This works well when you are invalidating a specific item but
//it fails when you invalidate sets, all related items to the set are not removed
public void PutItem(string key, object value,
IEnumerable<string> dependentEntitySets,
TimeSpan slidingExpiration, DateTime absoluteExpiration)
{
key = GetCacheKey(key);
_cache.Put(key, value, absoluteExpiration - DateTime.Now,
dependentEntitySets.Select(c => new DataCacheTag(c)).ToList());
foreach (var dep in dependentEntitySets)
{
CreateRegionIfNeeded(dep);
_cache.Put(key, " ", dep);
}
}
//!!!ISSUE #2 again: say i have a customer and orders.
//If I invalidate the order set then any related customer/order
//query should be removed. This just clears regions but doesnt remove the actual
//queries. This design will leave stale objects in cache
//!!!ISSUE #3: minor issue, but calling _cache.GetObjectsInRegion and iterating will
// cause unnecessary I/O. Changing this to calling region.Clear() will
// be more efficient.
public void InvalidateSets(IEnumerable<string> entitySets)
{
// Go through the list of objects in each of the set.
foreach (var dep in entitySets)
{
foreach (var val in _cache.GetObjectsInRegion(dep))
{
_cache.Remove(val.Key);
}
}
}
public void InvalidateItem(string key)
{
key = GetCacheKey(key);
DataCacheItem item = _cache.GetCacheItem(key);
_cache.Remove(key);
foreach (var tag in item.Tags)
{
_cache.Remove(key, tag.ToString());
}
}
//creates a hash of the query to store as the key
private static string GetCacheKey(string query)
{
byte[] bytes = Encoding.UTF8.GetBytes(query);
string hashString = Convert
.ToBase64String(MD5.Create().ComputeHash(bytes));
return hashString;
}
private void CreateRegionIfNeeded(string regionName)
{
try
{
_cache.CreateRegion(regionName);
}
catch (DataCacheException de)
{
if (de.ErrorCode != DataCacheErrorCode.RegionAlreadyExists)
{
throw;
}
}
}
}
}