2012-01-25 12 views
11

Necesito agregar funcionalidad de caché y encontré una nueva clase brillante llamada MemoryCache. Sin embargo, creo que MemoryCache está un poco tullido (necesito la funcionalidad de las regiones). Entre otras cosas, necesito agregar algo como ClearAll (región). Los autores hicieron un gran esfuerzo para mantener esta clase sin soporte de regiones, como:MemoryCache con soporte de regiones?

if (regionName != null) 
{ 
throw new NotSupportedException(R.RegionName_not_supported); 
} 

vuela en casi todos los métodos. No veo una manera fácil de anular este comportamiento. La única forma de agregar soporte regional que puedo pensar es agregar una nueva clase como un contenedor de MemoryCache en lugar de como una clase que hereda de MemoryCache. Luego, en esta nueva clase, cree un diccionario y deje que cada método "tampone" las llamadas a la región. Suena desagradable e incorrecto, pero finalmente ...

¿Conoces mejores formas de agregar regiones a MemoryCache?

Respuesta

5

Puede crear más de una sola instancia de MemoryCache, una para cada partición de sus datos.

http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache.aspx:

puede crear varias instancias de la clase MemoryCache para su uso en la misma aplicación y en la misma instancia dominio de aplicación

+0

, donde es el mejor lugar para crear instancias de todas estas nuevas instancias MemoryCache? ¿Hay un proveedor de MemoryCache donde pueda administrar todas estas instancias? –

+0

@HenleyChiu No creo que haya nada en las bibliotecas base. Simplemente use los medios estándar para compartir estado, como p. un estático, globalmente visible [ConcurrentDictionary ] (http://msdn.microsoft.com/en-us/library/dd287191.aspx) –

+2

Usar instancias múltiples de 'MemoryCache' puede reducir la efectividad del almacenamiento en caché en algunos circunstancias. Ver: http://stackoverflow.com/questions/8463962/using-multiple-instances-of-memorycache – Nathan

9

Sé que es mucho tiempo ya que preguntas esta pregunta, por lo que esta no es realmente una respuesta para usted, sino más bien una adición para los lectores futuros.

También me sorprendió descubrir que la implementación estándar de MemoryCache NO admite regiones. Hubiera sido tan fácil de proporcionar de inmediato. Por lo tanto, decidí incluir MemoryCache en mi propia clase simple para proporcionar la funcionalidad que a menudo necesito.

Adjunto mi código aquí para ahorrar tiempo para otros que tengan la misma necesidad.

/// <summary> 
/// ================================================================================================================= 
/// This is a static encapsulation of the Framework provided MemoryCache to make it easier to use. 
/// - Keys can be of any type, not just strings. 
/// - A typed Get method is provided for the common case where type of retrieved item actually is known. 
/// - Exists method is provided. 
/// - Except for the Set method with custom policy, some specific Set methods are also provided for convenience. 
/// - One SetAbsolute method with remove callback is provided as an example. 
/// The Set method can also be used for custom remove/update monitoring. 
/// - Domain (or "region") functionality missing in default MemoryCache is provided. 
/// This is very useful when adding items with identical keys but belonging to different domains. 
/// Example: "Customer" with Id=1, and "Product" with Id=1 
/// ================================================================================================================= 
/// </summary> 
public static class MyCache 
{ 
    private const string KeySeparator = "_"; 
    private const string DefaultDomain = "DefaultDomain"; 


    private static MemoryCache Cache 
    { 
     get { return MemoryCache.Default; } 
    } 

    // ----------------------------------------------------------------------------------------------------------------------------- 
    // The default instance of the MemoryCache is used. 
    // Memory usage can be configured in standard config file. 
    // ----------------------------------------------------------------------------------------------------------------------------- 
    // cacheMemoryLimitMegabytes: The amount of maximum memory size to be used. Specified in megabytes. 
    //        The default is zero, which indicates that the MemoryCache instance manages its own memory 
    //        based on the amount of memory that is installed on the computer. 
    // physicalMemoryPercentage: The percentage of physical memory that the cache can use. It is specified as an integer value from 1 to 100. 
    //        The default is zero, which indicates that the MemoryCache instance manages its own memory 
    //        based on the amount of memory that is installed on the computer. 
    // pollingInterval:    The time interval after which the cache implementation compares the current memory load with the 
    //        absolute and percentage-based memory limits that are set for the cache instance. 
    //        The default is two minutes. 
    // ----------------------------------------------------------------------------------------------------------------------------- 
    // <configuration> 
    // <system.runtime.caching> 
    //  <memoryCache> 
    //  <namedCaches> 
    //   <add name="default" cacheMemoryLimitMegabytes="0" physicalMemoryPercentage="0" pollingInterval="00:02:00" /> 
    //  </namedCaches> 
    //  </memoryCache> 
    // </system.runtime.caching> 
    // </configuration> 
    // ----------------------------------------------------------------------------------------------------------------------------- 



    /// <summary> 
    /// Store an object and let it stay in cache until manually removed. 
    /// </summary> 
    public static void SetPermanent(string key, object data, string domain = null) 
    { 
     CacheItemPolicy policy = new CacheItemPolicy { }; 
     Set(key, data, policy, domain); 
    } 

    /// <summary> 
    /// Store an object and let it stay in cache x minutes from write. 
    /// </summary> 
    public static void SetAbsolute(string key, object data, double minutes, string domain = null) 
    { 
     CacheItemPolicy policy = new CacheItemPolicy { AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(minutes) }; 
     Set(key, data, policy, domain); 
    } 

    /// <summary> 
    /// Store an object and let it stay in cache x minutes from write. 
    /// callback is a method to be triggered when item is removed 
    /// </summary> 
    public static void SetAbsolute(string key, object data, double minutes, CacheEntryRemovedCallback callback, string domain = null) 
    { 
     CacheItemPolicy policy = new CacheItemPolicy { AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(minutes), RemovedCallback = callback }; 
     Set(key, data, policy, domain); 
    } 

    /// <summary> 
    /// Store an object and let it stay in cache x minutes from last write or read. 
    /// </summary> 
    public static void SetSliding(object key, object data, double minutes, string domain = null) 
    { 
     CacheItemPolicy policy = new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(minutes) }; 
     Set(key, data, policy, domain); 
    } 

    /// <summary> 
    /// Store an item and let it stay in cache according to specified policy. 
    /// </summary> 
    /// <param name="key">Key within specified domain</param> 
    /// <param name="data">Object to store</param> 
    /// <param name="policy">CacheItemPolicy</param> 
    /// <param name="domain">NULL will fallback to default domain</param> 
    public static void Set(object key, object data, CacheItemPolicy policy, string domain = null) 
    { 
     Cache.Add(CombinedKey(key, domain), data, policy); 
    } 




    /// <summary> 
    /// Get typed item from cache. 
    /// </summary> 
    /// <param name="key">Key within specified domain</param> 
    /// <param name="domain">NULL will fallback to default domain</param> 
    public static T Get<T>(object key, string domain = null) 
    { 
     return (T)Get(key, domain); 
    } 

    /// <summary> 
    /// Get item from cache. 
    /// </summary> 
    /// <param name="key">Key within specified domain</param> 
    /// <param name="domain">NULL will fallback to default domain</param> 
    public static object Get(object key, string domain = null) 
    { 
     return Cache.Get(CombinedKey(key, domain)); 
    } 

    /// <summary> 
    /// Check if item exists in cache. 
    /// </summary> 
    /// <param name="key">Key within specified domain</param> 
    /// <param name="domain">NULL will fallback to default domain</param> 
    public static bool Exists(object key, string domain = null) 
    { 
     return Cache[CombinedKey(key, domain)] != null; 
    } 

    /// <summary> 
    /// Remove item from cache. 
    /// </summary> 
    /// <param name="key">Key within specified domain</param> 
    /// <param name="domain">NULL will fallback to default domain</param> 
    public static void Remove(object key, string domain = null) 
    { 
     Cache.Remove(CombinedKey(key, domain)); 
    } 



    #region Support Methods 

    /// <summary> 
    /// Parse domain from combinedKey. 
    /// This method is exposed publicly because it can be useful in callback methods. 
    /// The key property of the callback argument will in our case be the combinedKey. 
    /// To be interpreted, it needs to be split into domain and key with these parse methods. 
    /// </summary> 
    public static string ParseDomain(string combinedKey) 
    { 
     return combinedKey.Substring(0, combinedKey.IndexOf(KeySeparator)); 
    } 

    /// <summary> 
    /// Parse key from combinedKey. 
    /// This method is exposed publicly because it can be useful in callback methods. 
    /// The key property of the callback argument will in our case be the combinedKey. 
    /// To be interpreted, it needs to be split into domain and key with these parse methods. 
    /// </summary> 
    public static string ParseKey(string combinedKey) 
    { 
     return combinedKey.Substring(combinedKey.IndexOf(KeySeparator) + KeySeparator.Length); 
    } 

    /// <summary> 
    /// Create a combined key from given values. 
    /// The combined key is used when storing and retrieving from the inner MemoryCache instance. 
    /// Example: Product_76 
    /// </summary> 
    /// <param name="key">Key within specified domain</param> 
    /// <param name="domain">NULL will fallback to default domain</param> 
    private static string CombinedKey(object key, string domain) 
    { 
     return string.Format("{0}{1}{2}", string.IsNullOrEmpty(domain) ? DefaultDomain : domain, KeySeparator, key); 
    } 

    #endregion 

} 
+1

Enumerar a través de MemoryCache es ineficiente ya que bloqueará todo el caché por el tiempo. Además, Clear() es una búsqueda lineal, por lo que empeora con la cantidad de elementos de caché linealmente. Esta es una solución mejor: http://stackoverflow.com/a/22388943/220230 – Piedone

+0

Gracias por observar esto. En el ejemplo simple dado, ahora eliminé el método Clear para evitar desviar a otros. Para aquellos que realmente necesitan una forma de eliminar manualmente por región, me refiero al enlace dado. –

0

Otro enfoque consiste en implementar un contenedor alrededor de MemoryCache que implementa regiones mediante la composición de la clave y el nombre de la región, p.

public interface ICache 
{ 
... 
    object Get(string key, string regionName = null); 
... 
} 

public class MyCache : ICache 
{ 
    private readonly MemoryCache cache 

    public MyCache(MemoryCache cache) 
    { 
     this.cache = cache. 
    } 
... 
    public object Get(string key, string regionName = null) 
    { 
     var regionKey = RegionKey(key, regionName); 

     return cache.Get(regionKey); 
    } 

    private string RegionKey(string key, string regionName) 
    { 
     // NB Implements region as a suffix, for prefix, swap order in the format 
     return string.IsNullOrEmpty(regionName) ? key : string.Format("{0}{1}{2}", key, "::", regionName); 
    } 
... 
} 

No es perfecto pero funciona para la mayoría de los casos de uso.

he implementado este y está disponible como un paquete NuGet: Meerkat.Caching