问题 MemoryCache是​​否支持区域?


我需要添加缓存功能,并找到一个名为MemoryCache的新闪亮类。但是,我发现MemoryCache有点瘫痪(我需要区域功能)。除了其他东西,我需要添加像ClearAll(区域)这样的东西。作者努力保持这个类没有区域支持,代码如下:

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

几乎每种方法都有。 我没有看到一种简单的方法来覆盖这种行为。我能想到的添加区域支持的唯一方法是添加一个新类作为MemoryCache的包装,而不是作为一个继承自MemoryCache的类。然后在这个新类中创建一个Dictionary并让每个方法“缓冲”区域调用。听起来很讨厌和错误,但最终......

您知道向MemoryCache添加区域的更好方法吗?


9444
2018-01-25 13:41


起源



答案:


我知道你问这个问题已经很久了,所以这不是你的答案,而是未来读者的补充。

我也惊讶地发现MemoryCache的标准实现不支持区域。它很容易立即提供。因此,我决定将MemoryCache包装在我自己的简单类中,以提供我经常需要的功能。

我把我的代码放在这里,为有同样需要的人节省时间!

/// <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

}

9
2017-11-22 12:44



通过MemoryCache枚举效率很低,因为它会锁定整个缓存。你的Clear()也是一个线性搜索,因此线性增加了缓存项的数量。这是一个更好的解决方案: stackoverflow.com/a/22388943/220230 - Piedone
谢谢观察这个。在给定的简单示例中,我现在已经删除了Clear方法以避免将其他人引入歧途。对于那些真正需要按区域手动删除的方法,我可以参考给定的链接。 - Jakob Lithner


答案:


我知道你问这个问题已经很久了,所以这不是你的答案,而是未来读者的补充。

我也惊讶地发现MemoryCache的标准实现不支持区域。它很容易立即提供。因此,我决定将MemoryCache包装在我自己的简单类中,以提供我经常需要的功能。

我把我的代码放在这里,为有同样需要的人节省时间!

/// <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

}

9
2017-11-22 12:44



通过MemoryCache枚举效率很低,因为它会锁定整个缓存。你的Clear()也是一个线性搜索,因此线性增加了缓存项的数量。这是一个更好的解决方案: stackoverflow.com/a/22388943/220230 - Piedone
谢谢观察这个。在给定的简单示例中,我现在已经删除了Clear方法以避免将其他人引入歧途。对于那些真正需要按区域手动删除的方法,我可以参考给定的链接。 - Jakob Lithner


您可以创建多个MemoryCache实例,每个实例对应一个数据分区。

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

您可以创建MemoryCache类的多个实例,以便在同一个应用程序和同一AppDomain实例中使用


5
2018-02-10 05:19



哪里是实例化所有这些新MemoryCache实例的最佳位置?是否有MemoryCache提供程序,您可以在其中管理所有这些实例? - Henley Chiu
@HenleyChiu我认为基础库中没有任何东西。只需使用标准手段来共享状态,例如静态的,全局可见的 ConcurrentDictionary <string,MemoryCache> - Eugene Beresovsky
使用多个实例 MemoryCache 在某些情况下可能会降低缓存的效率。看到: stackoverflow.com/questions/8463962/... - Nathan


另一种方法是在MemoryCache周围实现一个包装器,它通过组合密钥和区域名称来实现区域,例如:

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

它并不完美,但它适用于大多数用例。

我实现了这个,它可以作为NuGet包使用: Meerkat.Caching


0
2018-05-18 11:08