「L2」キャッシュを行うためにEFプロバイダーラッパーをうまく採用しました: https://efwrappers.codeplex.com/
この設計では、複数の "キャッシュ プロバイダー" (インメモリ、asp.net キャッシュ) がサポートされています。ここで、共有キャッシュを使用して、これをクラスターに持ち込む必要があります。AppFabric をキャッシュ プロバイダーとして使用する方法を示す Julie Lerman の記事を見つけました: http://msdn.microsoft.com/en-us/magazine/hh394143.aspx
コードは MSDN の記事から引用されていますが、設計が壊れているようです。私は AppFabric の専門家ではありませんが、3 つの問題が見られます。そのうちの 1 つは、キャッシュ 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;
}
}
}
}
}