Генерация уникального ключа кеша на основе аргументов метода - PullRequest
5 голосов
/ 30 мая 2011

У меня есть базовая структура репозитория, которая в итоге выполняет запрос и отображает результаты обратно в объект:

Например:

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

Если это похоже на Dapper, то это потому, что под капотом GetItem оборачивает Dapper.

Я бы хотел добавить автоматическое кэширование в GetItem, у меня есть два аргумента:

  • Строка, содержащая запрос.
  • анонимный словарь, содержащий любые параметры.

Я обеспокоен тем, что простое простое хэширование этих параметров может привести к коллизиям ключей кеша, а когда вы извлекаете данные из кеша, коллизия может быть очень и очень плохой (т.е. чувствительна к утечкам). информация).

Итак, какие у меня есть методы, которые позволили бы генерировать ключ кэша разумного размера, гарантируя при этом уникальность, основанную на вводе запроса и параметров?

Ответы [ 2 ]

6 голосов
/ 30 мая 2011

Я использую следующие методы расширения для создания кэшированных версий делегатов:

    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}); 
}
3 голосов
/ 30 мая 2011

вижу несколько вариантов

  1. Упакуйте данные в класс, используйте BinaryFormatter для сериализации класса и выполните хеширование SHA1 для сериализованных данных, чтобы получить ключ хеш-функции.

  2. Упакуйте данные в класс, реализуйте IEqualityComparer, который затем можно сохранить в словаре. Реализуя IEqualityComparer, вы будете управлять генерацией хэша и выполнением сравнения данных для идентификации уникальных данных при возникновении коллизий.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...