根据方法参数生成唯一的缓存键

时间:2011-05-30 19:49:39

标签: c# .net asp.net caching

我有一个基本的存储库框架,最终执行查询并将结果映射回对象:

例如:

    public SomeEntity Get(id)
    {
        return base.GetItem<SomeEntity>
                   ("select * from SomeEntities where id = @idParam",
                    new { idParam = id}); 
    }

如果这看起来像Dapper,那是因为引擎盖下的GetItem正在包装Dapper。

我想为GetItem添加自动缓存,我有两个参数:

  • 包含查询的字符串。
  • 包含任何参数的匿名字典。

我担心对这些参数执行简单的主要哈希会导致缓存密钥冲突,当您从缓存中提取数据时,冲突可能会非常糟糕(I.E.泄漏敏感 信息)。

那么,我有哪些技术可以生成合理大小的缓存键,同时保证基于查询和参数输入的唯一性?

2 个答案:

答案 0 :(得分:6)

我使用以下扩展方法来制作委托的缓存版本:

    public static Func<T, TResult> AsCached<T, TResult>(this Func<T, TResult> function)
    {
        var cachedResults = new Dictionary<T, TResult>();
        return (argument) =>
        {
            TResult result;
            lock (cachedResults)
            {
                if (!cachedResults.TryGetValue(argument, out result))
                {
                    result = function(argument);
                    cachedResults.Add(argument, result);
                }
            }
            return result;
        };
    }

    public static Func<T1, T2, TResult> AsCached<T1, T2, TResult>(this Func<T1, T2, TResult> function)
    {
        var cachedResults = new Dictionary<Tuple<T1, T2>, TResult>();
        return (value1, value2) =>
        {
            TResult result;
            var paramsTuple = new Tuple<T1, T2>(value1, value2);
            lock(cachedResults)
            {
                if (!cachedResults.TryGetValue(paramsTuple, out result))
                {
                    result = function(value1, value2);
                    cachedResults.Add(paramsTuple, result);
                }
            }
            return result;
        };
    }

    public static Func<T1, T2, T3, TResult> AsCached<T1, T2, T3, TResult>(this Func<T1, T2, T3, TResult> function)
    {
        var cachedResults = new Dictionary<Tuple<T1, T2, T3>, TResult>();
        return (value1, value2, value3) =>
        {
            TResult result;
            var paramsTuple = new Tuple<T1, T2, T3>(value1, value2, value3);
            lock(cachedResults)
            {
                if (!cachedResults.TryGetValue(paramsTuple, out result))
                {
                    result = function(value1, value2, value3);
                    cachedResults.Add(paramsTuple, result);
                }
            }
            return result;
        };
    }

等等N个参数...

如果从代码中不清楚,我使用参数创建一个元组,并使用元组作为字典的键,该字典包含每组参数的返回值。请注意,每次调用AsCached时,都会创建一个单独的缓存。

您可以按如下方式使用这些方法:

private Func<int, SomeEntity> _getCached;

public SomeEntity Get(int id)
{
    if (_getCached == null)
    {
        Func<int, SomeEntity> func = GetImpl;
        _getCached = func.AsCached();
    }
    return _getCached(id);
}

private SomeEntity GetImpl(int id)
{
    return base.GetItem<SomeEntity>
               ("select * from SomeEntities where id = @idParam",
                new { idParam = id}); 
}

答案 1 :(得分:3)

我看到了一些选项

  1. 将数据打包到类中,使用BinaryFormatter序列化类并对序列化数据执行SHA1哈希,以便为您提供哈希键。

  2. 将数据打包成一个类,实现IEqualityComparer,然后将其存储在Dictionary中。通过实现IEqualityComparer,您将控制Hash的生成以及执行的数据比较,以便在发生冲突时识别唯一数据。