2011-05-30 10 views
5

tengo un marco repositorio básico que finalmente ejecuta una consulta y los mapas de los resultados de nuevo en un objeto:Generación de una clave de caché único basado en los argumentos del método

Por ejemplo:

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

Si esto parece Dapper, es porque debajo del capó GetItem está envolviendo a Dapper.

me gustaría añadir almacenamiento en caché automático para GetItem, que tienen dos argumentos que vienen en:

  • Una cadena que contiene la consulta.
  • un diccionario anónimo que contiene los parámetros.

Me preocupa que haciendo una simple almohadilla de primera en estos parámetros podría provocar colisiones clave de caché, y cuando usted está tirando de datos de una memoria caché, una colisión puede ser muy muy malo (POR EJEMPLO fugas sensible información).

Entonces, ¿qué técnicas tengo que generen una clave de memoria caché de tamaño razonable, al tiempo que garantiza la exclusividad en función de la entrada de una consulta y los parámetros?

+0

A mí parece que se está describiendo un mapa de la identidad de las clases. En este caso, la clave puede ser simplemente la manecilla de tiro combinada con la identificación. Lo que pasa con los mapas de identidad es que quieres asegurarte de que están ligados a algún tipo de contexto, un mapa de identidad global es increíblemente difícil de invalidar –

Respuesta

6

que utilizan los siguientes métodos de extensión para hacer versiones en caché de los delegados:

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; 
     }; 
    } 

y así sucesivamente para los parámetros N ...

En caso de que no está claro a partir del código, lo cree una tupla con los argumentos y use la tupla como la clave de un diccionario que contiene los valores de retorno para cada conjunto de argumentos. Tenga en cuenta que cada vez que llama al AsCached, crea un caché por separado.

Puede utilizar estos métodos de la siguiente manera:

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}); 
} 
2

veo algunas opciones

  1. paquete de los datos en una clase, se utiliza un BinaryFormatter para serializar la clase y realizar un hash SHA1 de los datos serializados a darle una clave hash.

  2. Pack los datos en una clase, implementa IEqualityComparer que luego se puede almacenar en un diccionario. Al implementar IEqualityComparer, controlará la generación de Hash y la comparación de datos realizada para identificar los datos únicos cuando se producen colisiones.

Cuestiones relacionadas