工作中需要用到內存緩存,最開始打算用個的是.net自帶的MemoryCache這麼個東西,用的時候發現在服務端有時會莫名其妙的丟失緩存並且丟失後就緩存不上了。本來網上關於使用MemoryCache的不多,一直也沒有找到原因和解決辦法,所以就自己仿着它寫了一個簡單的實現。
首先看下目錄結構
其中:
ChangeMoniter:是緩存對象的過期策略的檢測器,包括文件改變監視器(FileChangeMoniter)和時間改變監視器(TimeChangeMoniter)
IMoniter:監視器接口,用來實現自定義擴展的監視器
IRemoveCache:定義了調用了MemoryCache回調函數刪除緩存對象的接口。
MemoryCache:內存緩存類。
MemoryCacheEntry:緩存對象類。
MemoryCachePolicy:緩存過期策略類。
MemoryCacheManager:緩存管理器類。
FileChangeMoniter
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using Cache.MemoryCache.Interface;
namespace Cache.MemoryCache.Moniter
{
public class FileChangeMoniter:IMoniter
{
#region 變量
private FileSystemWatcher fileWatcher = null;//文件監視器對象
private string _filePath = string.Empty;
private DateTime _activityTime;//對象的激活時間
private bool _isRemove = false;//該緩存對象是否移除
#endregion
#region 屬性
/// <summary>
/// 文件路徑
/// </summary>
public string FilePath
{
get { return _filePath; }
}
public DateTime ActivityTime
{
get { return _activityTime; }
}
#endregion
#region 構造函數
public FileChangeMoniter(string filePath)
{
this._filePath = filePath;
SetFileWatcher();
}
#endregion
#region 文件監視器
/// <summary>
/// 設置文件監視器
/// </summary>
private void SetFileWatcher()
{
fileWatcher = new FileSystemWatcher();
fileWatcher.Path = this.FilePath.Substring(0, this.FilePath.LastIndexOf(@"\"));
fileWatcher.Filter = this.FilePath.Substring(this.FilePath.LastIndexOf(@"\") + 1, this.FilePath.Length - 1 - this.FilePath.LastIndexOf(@"\"));
fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
fileWatcher.EnableRaisingEvents = true;
fileWatcher.Changed += fileWatcher_Changed;
}
/// <summary>
/// 文件修改事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void fileWatcher_Changed(object sender, FileSystemEventArgs e)
{
_isRemove = true;
}
#endregion
#region IMoniter接口方法
// 判斷對象是否過期
public bool IsExpire()
{
return this._isRemove;
}
// 刷新對象激活時間
public void RefreshActivityTime(DateTime time)
{
this._activityTime = time;
}
#endregion
TimeChangeMoniter
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cache.MemoryCache.Interface;
namespace Cache.MemoryCache.Moniter
{
public class TimeChangeMonitor:IMoniter
{
#region 變量
private DateTime _createTime;//創建時間
private DateTime _activityTime;//激活時間
private DateTime _disposeTime;//消除時間
private TimeSpan _spanTime;//相對時間
private TimeType _timeType;
private bool _isRemove = false;
#endregion
#region 屬性
public DateTime CreateTime
{
get { return _createTime; }
}
public DateTime ActivityTime
{
get { return _activityTime; }
}
public DateTime DisposeTime
{
get { return _disposeTime; }
}
public TimeSpan SpanTime
{
get { return _spanTime; }
}
public TimeType TimeType
{
get { return _timeType; }
}
#endregion
#region 構造函數
public TimeChangeMonitor(DateTime disposeTime)
{
this._createTime = DateTime.Now;
this._activityTime = this._createTime;
this._disposeTime = disposeTime;
this._timeType = TimeType.Absolute_Time;
}
public TimeChangeMonitor(TimeSpan spanTime)
{
this._createTime = DateTime.Now;
this._activityTime = this._createTime;
this._spanTime = spanTime;
this._timeType = TimeType.Relative_Time;
}
#endregion
/// <summary>比較絕對時間的過期策略</summary>
/// <returns>過期:true,不過期:false</returns>
private bool SetAbsoluteTime()
{
DateTime nowTime = DateTime.Now;
if (nowTime.CompareTo(DisposeTime) < 0)
{
return false;
}
else
{
return true;
}
}
/// <summary>比較相對時間的過期策略</summary>
/// <returns>過期:true,不過期:false</returns>
private bool SetRelativeTime()
{
DateTime endTime = this._activityTime + this._spanTime;
DateTime nowTime = DateTime.Now;
if (nowTime.CompareTo(endTime) < 0)
{
return false;
}
else
{
return true;
}
}
#region IMoniter接口方法
// 判斷對象是否過期
public bool IsExpire()
{
switch (_timeType)
{
case TimeType.Absolute_Time://絕對時間
_isRemove = SetAbsoluteTime();
break;
case TimeType.Relative_Time://相對時間
_isRemove = SetRelativeTime();
break;
}
return _isRemove;
}
//刷新對象的激活時間
public void RefreshActivityTime(DateTime time)
{
this._activityTime = time;
}
#endregion
IMoniter和IRemoveCache接口如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Cache.MemoryCache.Interface
{
/// <summary>
/// Moniter的回調方法接口
/// </summary>
public interface IMoniter
{
/// <summary>
/// 當前對象是否過期
/// </summary>
/// <returns></returns>
bool IsExpire();
/// <summary>
/// 刷新激活時間
/// </summary>
void RefreshActivityTime(DateTime time);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Cache.MemoryCache.Interface
{
/// <summary>
/// 定義了調用了MemoryCache回調函數刪除緩存對象的接口
/// </summary>
internal interface IRemoveCache
{
/// <summary>
/// 判斷緩存對象是否過期並移除
/// </summary>
/// <returns></returns>
void CheckExpireAndRemove();
}
}
MemoryCache
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cache.MemoryCache.Interface;
using Cache.MemoryCache.Moniter;
namespace Cache.MemoryCache
{
public class MemoryCache
{
#region 屬性
private string _name = string.Empty;
private static readonly object lockobj = new object();
public string Name
{
get { return _name; }
}
private Dictionary<string, MemoryCacheEntry> _cache;
#endregion
#region 構造函數
private MemoryCache(string cacheName)
{
this._name = cacheName;
this._cache = new Dictionary<string, MemoryCacheEntry>();
MemoryCacheManager.GetManager().Add(this);
}
#endregion
#region 默認的MemoryCache
private static MemoryCache _defaultMemoryCache;
/// <summary>默認的MemoryCache</summary>
public static MemoryCache DefaultCache
{
get
{
lock (lockobj)//多線程併發鎖
{
if (_defaultMemoryCache == null)
{
_defaultMemoryCache = new MemoryCache("Default");
}
}
return _defaultMemoryCache;
}
}
#endregion
#region 創建一個新的非默認的MemoryCache
/// <summary>
/// 該方法是獲取一個新緩存對象
/// </summary>
/// <param name="cacheName">MemoryCache名字</param>
/// <returns></returns>
public static MemoryCache GetMemoryCache(string cacheName)
{
if (MemoryCacheManager.GetManager().ContainCache(cacheName))
{
return MemoryCacheManager.GetManager().Get(cacheName);
}
else
{
return new MemoryCache(cacheName);
}
}
#endregion
#region 緩存對象操作方法
/// <summary>將對象放入緩存</summary>
/// <param name="key">緩存對象的key</param>
/// <param name="value">需要緩存的對象</param>
/// <param name="policy">緩存的處理策略</param>
public void SetCache(string key, object value, MemoryCachePolicy policy)
{
lock (lockobj)
{
MemoryCacheEntry cacheEntry = new MemoryCacheEntry(this, key, value, policy);
this._cache.Add(key, cacheEntry);
//刷新激活時間
cacheEntry.RefreshActivityTime();
}
}
/// <summary>獲取緩存對象</summary>
/// <typeparam name="T">緩存對象類型</typeparam>
/// <param name="key">緩存對象的key</param>
/// <returns></returns>
public T GetCache<T>(string key)
{
//判斷緩存是否過期
CheckExpireAndRemove(key);
if (_cache.ContainsKey(key))
{
MemoryCacheEntry cacheEntry = this._cache[key];
//刷新激活時間
cacheEntry.RefreshActivityTime();
return (T)cacheEntry.Value;
}
else
{
throw new Exception(string.Format("不存在key爲{0}的緩存對象", key));
}
}
/// <summary>從MemoryCache中移除緩存對象</summary>
/// <param name="key">緩存對象的key</param>
public void RemoveCache(string key)
{
if (_cache.ContainsKey(key))
{
_cache.Remove(key);
}
}
/// <summary>從MemoryCache中移除緩存對象(供MemoryCacheEntry的回調)</summary>
/// <param name="cacheName"></param>
/// <param name="key"></param>
internal void RemoveCache(string cacheName, string key)
{
MemoryCacheManager.GetManager().Get(cacheName).RemoveCache(key);
}
public bool ContainCache(string key)
{
//判斷緩存是否過期
CheckExpireAndRemove(key);
return _cache.ContainsKey(key);
}
/// <summary>
/// 判斷緩存對象是否過期並移除
/// </summary>
/// <returns></returns>
private void CheckExpireAndRemove(string key)
{
if (_cache.ContainsKey(key))
{
//MemoryCacheEntry cacheEntry = this._cache[key];
IRemoveCache cacheEntry = this._cache[key];
cacheEntry.CheckExpireAndRemove();
}
}
#endregion
}
}
MemoryCacheEntry
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cache.MemoryCache.Interface;
using Cache.MemoryCache.Moniter;
namespace Cache.MemoryCache
{
internal class MemoryCacheEntry:IRemoveCache
{
#region 變量
private MemoryCache _memoryCache;
#endregion
#region 屬性
public MemoryCachePolicy CachePolicy { set; get; }
public object Value { set; get; }
public string Key { set; get; }
#endregion
#region 構造函數
public MemoryCacheEntry(MemoryCache memoryCache, string key, object value, MemoryCachePolicy policy)
{
this._memoryCache = memoryCache;
this.Key = key;
this.Value = value;
this.CachePolicy = policy;
}
#endregion
/// <summary> 刷新對象激活時間 </summary>
public void RefreshActivityTime()
{
switch (CachePolicy.PolicyType)
{
case PolicyType.FileChange:
CachePolicy.FileChangeMoniter.RefreshActivityTime(DateTime.Now);
break;
case PolicyType.TimeChange:
CachePolicy.TimeChangeMoniter.RefreshActivityTime(DateTime.Now);
break;
case PolicyType.Custom:
CachePolicy.CustomChangeMoniters.RefreshActivityTime(DateTime.Now);
break;
}
}
/// <summary>刪除緩存對象</summary>
/// <param name="cacheName"></param>
/// <param name="key"></param>
private void RemoveCache(string cacheName, string key)
{
this._memoryCache.RemoveCache(cacheName, key);
}
#region IRemoveCache接口方法
public void CheckExpireAndRemove()
{
bool isRemove = false;
if (CachePolicy != null)
{
switch (CachePolicy.PolicyType)
{
case PolicyType.FileChange:
isRemove = CachePolicy.FileChangeMoniter.IsExpire();
break;
case PolicyType.TimeChange:
isRemove = CachePolicy.TimeChangeMoniter.IsExpire();
break;
case PolicyType.Custom:
isRemove = CachePolicy.FileChangeMoniter.IsExpire();
break;
}
}
if (isRemove)
{
//回調刪除緩存對象
RemoveCache(this._memoryCache.Name, Key);
}
}
#endregion
}
}
MemoryCachePolicy
using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cache.MemoryCache.Interface;
using Cache.MemoryCache.Moniter;
namespace Cache.MemoryCache
{
public class MemoryCachePolicy
{
#region 變量
private PolicyType _policyType;
#endregion
#region 屬性
/// <summary>
/// Policy類型
/// </summary>
public PolicyType PolicyType
{
get { return _policyType; }
}
/// <summary>
/// 文件改變Moniter
/// </summary>
public FileChangeMoniter FileChangeMoniter { set; get; }
/// <summary>
/// 時間改變Moniter
/// </summary>
public TimeChangeMonitor TimeChangeMoniter { set; get; }
/// <summary>
/// 用來自定義擴展的Moniter
/// </summary>
public IMoniter CustomChangeMoniters { set; get; }
#endregion
#region 構造函數
public MemoryCachePolicy(PolicyType type)
{
this._policyType = type;
}
#endregion
}
public enum PolicyType
{
FileChange = 0,
TimeChange = 1,
Custom = 2
}
}
MemoryCacheManager
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cache.MemoryCache.Interface;
using Cache.MemoryCache.Moniter;
namespace Cache.MemoryCache
{
/// <summary>
/// MemoryCache的管理工具類
/// 該類是單例類
/// </summary>
internal class MemoryCacheManager
{
private static MemoryCacheManager _memoryCacheManager;
private Dictionary<string, MemoryCache> _cacheManager = new Dictionary<string, MemoryCache>();
private static readonly object lockobj = new object();
private MemoryCacheManager() { }
public static MemoryCacheManager GetManager()
{
lock (lockobj)//控制多線程的併發問題
{
if (_memoryCacheManager == null)
{
_memoryCacheManager = new MemoryCacheManager();
}
}
return _memoryCacheManager;
}
/// <summary>將緩存對象存入緩存管理器</summary>
/// <param name="memoryCache">MemoryCache對象</param>
public void Add(MemoryCache memoryCache)
{
lock (lockobj)
{
if (_cacheManager.ContainsKey(memoryCache.Name))
{
_cacheManager.Remove(memoryCache.Name);
}
_cacheManager.Add(memoryCache.Name, memoryCache);
}
}
/// <summary>獲取memoryCacheName對應的MemoryCache對象</summary>
/// <param name="memoryCacheName">MemoryCache名稱</param>
/// <returns></returns>
public MemoryCache Get(string memoryCacheName)
{
if (_cacheManager.ContainsKey(memoryCacheName))
{
return _cacheManager[memoryCacheName];
}
else
{
throw new Exception("MemoryCache緩存對象找不到!");
//return default(MemoryCache);
}
}
/// <summary>判斷當前的MemoryCache是否存在</summary>
/// <param name="memoryCacheName"></param>
/// <returns></returns>
public bool ContainCache(string memoryCacheName)
{
if (_cacheManager.ContainsKey(memoryCacheName))
{
return true;
}
else
{
return false;
}
}
/// <summary>MemoryCacheManager索引器</summary>
/// <param name="memoryCacheName">MemoryCache名稱</param>
/// <returns></returns>
public MemoryCache this[string memoryCacheName]
{
get
{
if (_cacheManager.ContainsKey(memoryCacheName))
{
return _cacheManager[memoryCacheName];
}
else
{
throw new Exception("MemoryCache緩存對象找不到!");
//return default(MemoryCache);
}
}
}
}
}
Demo
MemoryCache cache = MemoryCache.DefaultCache;//獲取緩存
if (cache.ContainCache(CACHE_NAME) == false)//判斷當前key的緩存對象是否存在
{
//這裏處理自己的邏輯
MemoryCachePolicy policy = new MemoryCachePolicy(PolicyType.TimeChange);//設置緩存策略
TimeChangeMonitor moniter = new TimeChangeMonitor(TimeSpan.FromSeconds(600));//設置相對時間的時間監視器
//TimeChangeMonitor moniter = new TimeChangeMonitor(DateTime.Now.AddSeconds(600));//設置絕對時間的時間監視器
policy.TimeChangeMoniter = moniter;
//存入緩存
cache.SetCache(CACHE_NAME, dsCheck, policy);
}
else
{
//從緩存獲取數據
dsCheck = cache.GetCache<DataSet>(CACHE_NAME);;
}