使用JSON配置从C#管理Httpcache

时间:2016-09-26 16:27:33

标签: c# json http caching

我编写了一种有效的方法来控制使用JSON和C#控制它在HttpCache中缓存/不缓存的数据。

此实现的原因是利用现有的应用程序服务器来存储缓存,以节省网络延迟并跨进程调用。另一个原因是在我们发现内存或性能问题后最小化代码更改(因为您可以从JSON控制数据)。

该实现使用C#代码与HttpCache进行交互,并使用JSON对象以类似于我们已经看到的用于在Web应用程序项目中工作的表单身份验证的方式查找要缓存的内容。

JSON格式如下:

{
  Key: "Cache",
  Allow: "*",
  Deny: "",
  Keys: [
    {
      Key: "CacheKey",
      Allow: "*",
      Deny: "",
      Keys: [
        {
          Key: "Organization",
          Allow: "*",
          Deny: "",
          Keys: [
            {
              Key: "Department",
              Allow: "*",
            }
          ]
        }
      ]
    },
    {
      Key: "SomeOtherCacheKey",
      Allow: "*",
      Deny: ""
    }
  ]
}

1 个答案:

答案 0 :(得分:0)

我以组织和部门搜索的Employee课程为例。

虽然解决方案不是完全动态的,但它与存储库密切配合。

为了实现它,我创建了一个通用的CacheBase类。此类必须从将被调用以获取数据的外观派生,而外观将调用GetFromCache方法从缓存中获取数据。如果JSON不允许管理给定cacheKey的缓存以及参数(组键和值),则facade将直接调用存储库。我假设所需的参数将被注入构造函数。

欢迎编辑和代码审查。

public class EmployeeFacade: CacheBase<IEnumerable<Employee>>
{
    private readonly IEmployeeRepository _repository;
    public EmployeeFacade(IEmployeeRepository employeeRepository) : base("3600","Employee_Cache",cacheConfigurationJSON)
    {
        _repository = employeeRepository;
    }

  public method GetEmployee(string organization, string department)
  {
        var groupKeyAndValues = new List<Tuple<string, string>>();
        groupKeyAndValues.Add(new Tuple<String, string>("Organization", organization));
        groupKeyAndValues.Add(new Tuple<String, string>("Department", department));
        bool isCacheAllowed;

        var employeeInfos = base.GetFromCache(() => _repository.GetEmployee(organization, department), groupKeyAndValues, out isCacheAllowed);

        //if cache is not allowed for this combination , return direct from repository.
        if (!isCacheAllowed)
            return _repository.GetEmployee(organization, department);
  }
}

public abstract class CacheBase<T> where T : class
{
    private string _cacheTimeoutInSeconds;
    private string _cacheConfiguration;
    private static readonly object CacheLockObject = new object();
    string _cacheKey;
    public CacheBase(string cacheTimeoutInSeconds, string cacheKey, string cacheConfigurationJson)
    {
        _cacheTimeoutInSeconds= cacheTimeoutInSeconds;
        _cacheConfiguration = cacheConfigurationJson;
        _cacheKey = cacheKey;
    }

    public T GetFromCache(Func<T> methodToFillCache, IEnumerable<Tuple<string, string>> groupKeyAndValues, out bool isCacheAllowed)
    {
        var baseCacheKey = _cacheKey;
        var detailedCacheKey = GenerateCacheKey(groupKeyAndValues);
        isCacheAllowed = IsSaveToCacheAllowed(baseCacheKey, groupKeyAndValues);

        if (!isCacheAllowed) return null;

        var result = HttpRuntime.Cache[detailedCacheKey] as T;

        if (result == null)
        {
            lock (CacheLockObject)
            {
                if (result == null)
                {
                    result = methodToFillCache() as T;
                    SetToCache(result, detailedCacheKey);
                }
            }
        }
        return result;
    }

    private void SetToCache(T data, string detailedCacheKey)
    {

        HttpRuntime.Cache.Insert(detailedCacheKey, data, null,
            DateTime.Now.AddSeconds(Convert.ToInt32(cacheTimeoutInSeconds)), TimeSpan.Zero);
    }

    private string GenerateCacheKey(IEnumerable<Tuple<string, string>> groupKeyAndValues)
    {
        string detailedCacheKey = _cacheKey;
        if (groupKeyAndValues == null) return detailedCacheKey;
        var groupKeys = groupKeyAndValues.Select(x => x.Item1).ToList();
        if (groupKeys != null && groupKeys.Count > 0)
        {
            foreach (var groupKey in groupKeys)
            {
                if (!String.IsNullOrWhiteSpace(groupKey))
                    detailedCacheKey += "_" + groupKey;
            }
        }
        return detailedCacheKey;
    }

    private bool IsSaveToCacheAllowed(string cacheKey, IEnumerable<Tuple<string, string>> groupKeyAndValues)
    {
        cacheKey = cacheKey.ToUpper();

        if (_cacheConfiguration == null) return false;

        string valuesAllowed = CacheConstant.All; string valuesDenied = CacheConstant.All;

        //CACHE                                                                 
        if (_cacheConfiguration == null) return true;
        valuesAllowed = _cacheConfiguration.Allow;
        valuesDenied = _cacheConfiguration.Deny;

        var matchingCacheConfig = _cacheConfiguration.Keys.FirstOrDefault(x => x.Key.ToUpper() == cacheKey);
        if (matchingCacheConfig == null) return IsAllowed(cacheKey, valuesAllowed, valuesDenied); ;
        valuesAllowed = matchingCacheConfig.Allow;
        valuesDenied = matchingCacheConfig.Deny;

        bool isAllowed = IsAllowed(cacheKey, valuesAllowed, valuesDenied);                                                                               
        foreach (var groupKeyAndValue in groupKeyAndValues)
        {
            var key = groupKeyAndValue.Item1.ToUpper();
            var value = groupKeyAndValue.Item2.ToUpper();
            matchingCacheConfig = matchingCacheConfig.Keys.FirstOrDefault(x => x.Key.ToUpper() == key);

            if (matchingCacheConfig == null) return IsAllowed(key, valuesAllowed, valuesDenied);
            valuesAllowed = matchingCacheConfig.Allow;
            valuesDenied = matchingCacheConfig.Deny;

            isAllowed = IsAllowed(value, matchingCacheConfig.Allow, matchingCacheConfig.Deny);

            if (!isAllowed) break;
        }
        return isAllowed;
    }

    bool IsAllowed(string value, string allowedValues, string deniedValues)
    {
        //Prerequisites: among allowed and denied values , one should contain * and other should contain value or empty string 
        var allowed = allowedValues.ToUpper().Split(',');
        var denied = deniedValues.ToUpper().Split(',');
        if (denied.Contains(CacheConstant.All))
        {
            if (allowed.Contains(value) || allowed.Contains(CacheConstant.All))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else if (denied.Contains(value))
        {
            if (allowed.Contains(value))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else //denied contains nothing
        {
            if (allowed.Contains(value) || allowed.Contains(CacheConstant.All) || allowed.Count() == 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}