4

我已成功使用 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;
        }
      }
    }
  }
}
4

0 回答 0