2015-11-14 59 views
-1

我正在與MemoryCache一起工作。MemoryCache的SlidingExpiration - 可選幻燈片過期?

我創建了高速緩存,並使用5分鐘的滑動過期紛紛加入到一個條目:

MemoryCache.Default.Set("Key", "Value", new CacheItemPolicy 
{ 
    SlidingExpiration = new TimeSpan(0, 5, 0) 
}); 

此項目將會從緩存中刪除,如果未在5分鐘之內訪問。如果被訪問則滑動到期計時器重置爲5分鐘:

// Resets the sliding expiration: 
MemoryCache.Default.Get("Key"); 

我希望能夠有選擇地從緩存中檢索,無需重新設置滑動到期定時器的條目。

它似乎不可能,但我想確認。


爲了澄清我的特殊需求:

  • 我有兩個實體,報告和ReportData。 ReportData查詢速度慢。 Report和ReportData都被緩存到兩個獨立的MemoryCaches中。

  • 報告MemoryCache在四天後過期。 ReportData MemoryCache在30分鐘後過期。

  • 只要ReportData自然到期,它就會自動刷新並重新緩存。這確保所有ReportData條目都是新鮮的。

  • 如果用戶在4天內未請求報告,則將其從緩存中刪除,並刪除其相應的ReportData。每當用戶請求報告時,這4天計時器應該重新啓動。

的問題是:刷新ReportData要求報告的參考。通過其緩存獲取對報告的引用會導致緩存計時器重新啓動。這是不希望的。報告緩存計時器只應在用戶請求報告時重新啓動。

一個潛在的解決方案是引入第三個緩存。另一個緩存允許外部訪問與內部訪問不同的到期行爲。

這裏是我當前的代碼:

/// <summary> 
/// A service for caching Custom reports. 
/// </summary> 
public class ReportCachingService : ServiceBase 
{ 
    /// <summary> 
    /// Refresh report data every N minutes. 
    /// </summary> 
    private int ReportDataRefreshInterval { get; set; } 

    /// <summary> 
    /// Remember reports for N days. 
    /// </summary> 
    private int MaxReportAge { get; set; } 
    private MemoryCache ReportDataCache { get; set; } 
    private MemoryCache ReportCache { get; set; } 
    private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 

    public ReportCachingService() 
    { 
     Logger.Info("ReportCachingService initializing..."); 
     LoadConfiguration(); 
     // Note: The name 'ReportDataCache' must be kept in-sync w/ App.config namedCache entry. 
     ReportDataCache = new MemoryCache("ReportDataCache"); 
     ReportCache = new MemoryCache("ReportCache"); 
     Logger.Info("ReportCachingService successfully started."); 
    } 

    public ReportData GetReportData(int reportID) 
    { 
     string key = reportID.ToString(); 
     ReportData reportData = ReportDataCache.Get(key) as ReportData ?? GetAndCacheReportData(reportID); 

     return reportData; 
    } 

    public Report GetReport(int reportID) 
    { 
     string key = reportID.ToString(); 
     Report report = ReportCache.Get(key) as Report ?? GetAndCacheReport(reportID); 

     return report; 
    } 

    private void LoadConfiguration() 
    { 
     try 
     { 
      ReportDataRefreshInterval = GetConfigValue("ReportDataRefreshInterval"); 
      MaxReportAge = GetConfigValue("MaxReportAge"); ; 
      Logger.Info(string.Format("Configuration loaded. Report data will refresh every {0} minutes. Maximum report age is {1} day(s).", ReportDataRefreshInterval, MaxReportAge)); 
     } 
     catch (Exception exception) 
     { 
      Logger.Error("Error loading configuration.", exception); 
      throw; 
     } 
    } 

    private static int GetConfigValue(string key) 
    { 
     string configValueString = ConfigurationManager.AppSettings[key]; 
     if (string.IsNullOrEmpty(configValueString)) 
     { 
      throw new Exception(string.Format("Failed to find {0} in App.config", key)); 
     } 

     int configValue; 
     bool isValidConfigValue = int.TryParse(configValueString, out configValue); 

     if (!isValidConfigValue) 
     { 
      throw new Exception(string.Format("{0} was found in App.config, but is not a valid integer value.", key)); 
     } 

     return configValue; 
    } 

    private ReportData GetAndCacheReportData(int reportID) 
    { 
     Report report = GetReport(reportID); 
     ReportData reportData = report.GetData(false, "Administrator"); 

     if (reportData == null) 
     { 
      string errorMessage = string.Format("Failed to find reportData for report with ID: {0}", reportID); 
      Logger.Error(errorMessage); 
      throw new Exception(errorMessage); 
     } 

     // SlidingExpiration forces cache expiration to refresh when an entry is accessed. 
     TimeSpan reportDataCacheExpiration = new TimeSpan(0, ReportDataRefreshInterval, 0); 
     string key = reportID.ToString(); 
     ReportDataCache.Set(key, reportData, new CacheItemPolicy 
     { 
      SlidingExpiration = reportDataCacheExpiration, 
      UpdateCallback = OnReportDataUpdate 
     }); 

     return reportData; 
    } 

    private Report GetAndCacheReport(int reportID) 
    { 
     // If the ReportCache does not contain the Report - attempt to load it from DB. 
     Report report = Report.Load(reportID); 

     if (report == null) 
     { 
      string errorMessage = string.Format("Failed to find report with ID: {0}", reportID); 
      Logger.Error(errorMessage); 
      throw new Exception(errorMessage); 
     } 

     // SlidingExpiration forces cache expiration to refresh when an entry is accessed. 
     TimeSpan reportCacheExpiration = new TimeSpan(MaxReportAge, 0, 0, 0); 
     string key = reportID.ToString(); 
     ReportCache.Set(key, report, new CacheItemPolicy 
     { 
      SlidingExpiration = reportCacheExpiration, 
      RemovedCallback = OnReportRemoved 
     }); 

     return report; 
    } 

    private void OnReportRemoved(CacheEntryRemovedArguments arguments) 
    { 
     Logger.DebugFormat("Report with ID {0} has expired with reason: {1}.", arguments.CacheItem.Key, arguments.RemovedReason); 
     // Clear known ReportData for a given Report whenever the Report expires. 
     ReportDataCache.Remove(arguments.CacheItem.Key); 
    } 

    private void OnReportDataUpdate(CacheEntryUpdateArguments arguments) 
    { 
     Logger.DebugFormat("ReportData for report with ID {0} has updated with reason: {1}.", arguments.UpdatedCacheItem.Key, arguments.RemovedReason); 
     // Expired ReportData should be automatically refreshed by loading fresh values from the DB. 
     if (arguments.RemovedReason == CacheEntryRemovedReason.Expired) 
     { 
      int reportID = int.Parse(arguments.Key); 
      GetAndCacheReportData(reportID); 
     } 
    } 
} 
+0

我不認爲你可以通過這種方式使用內存緩存實現你想要的。看到我的代碼如下。 –

+0

這是可能的。你在這裏看到的是SlidingExpiration的行爲。您必須改用AbsoluteExpiration。 –

回答

-1

這是更好地使用一個單獨的類的內存緩存。這是一個示例。

public class MyMemoryCache 
{ 
    private readonly ObjectCache _cache; 

    public MyMemoryCache() 
    { 
     _cache = MemoryCache.Default; 
    } 

    /// <summary> 
    /// Get an object from cache 
    /// </summary> 
    /// <param name="cacheKey"></param> 
    /// <returns></returns> 
    public object Get(string cacheKey) 
    { 
     return _cache.Get(cacheKey); 
    } 

    /// <summary> 
    /// check if the object is available in the cache 
    /// </summary> 
    /// <param name="cacheKey"></param> 
    /// <returns></returns> 
    public bool Contains(string cacheKey) 
    { 
     return _cache.Contains(cacheKey); 
    } 

    /// <summary> 
    /// Add an objct in to the cache 
    /// </summary> 
    /// <param name="objectToBeChached"></param> 
    /// <param name="cacheKey"></param> 
    public void Add(string cacheKey, object objectToBeChached) 
    { 
     var cacheItemPolicy = new CacheItemPolicy { AbsoluteExpiration = DateTime.Now.AddMinutes(5.0) }; 
     _cache.Add(cacheKey, objectToBeChached, cacheItemPolicy); 
    } 
} 
+0

我不明白這是如何幫助我的問題。你能詳細說明嗎? –

+0

你看過[msdn示例](https://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache(v = vs.110).aspx)嗎?你錯過了MemoryCache.Default。您還需要設置到期政策。 –

+0

我在發佈之前已經閱讀了MemoryCache的全部MSDN條目。我的帖子中的第一個代碼片段引用了MemoryCache。缺省爲示例僞代碼,但我的完整代碼示例在不使用默認緩存的情況下構建顯式MemoryCache對象。 無論如何...我的問題是參考SlidingExpiration交互,這與我是否使用默認緩存或我自己無關。 –