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

Jon*_*and 5 .net c# asp.net caching

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

例如:

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

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

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

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

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

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

Tho*_*que 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;
        };
    }
Run Code Online (Sandbox Code Playgroud)

对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}); 
}
Run Code Online (Sandbox Code Playgroud)