[英]Generic caching in c#
有沒有更有效的方法來檢查緩存的數據是否存在,是否確實得到了它,以及它是否沒有調用 api/database 然后緩存它? 一遍又一遍地執行這樣的代碼對我來說似乎非常低效。
List<Map> maps = new List<Map>();
List<Playlist> playlists = new List<Playlist>();
if (SingletonCacheManager.Instance.Get<List<Map>>("Maps") != null)
{
maps = SingletonCacheManager.Instance.Get<ListMap>>("Maps");
}
else
{
maps = _mapRepository.FindBy(x => x.Active).ToList();
SingletonCacheManager.Instance.Add<List<Map>>(maps, "Maps", 20);
}
if (SingletonCacheManager.Instance.Get<List<Playlist>>("Playlists") != null)
{
playlists = SingletonCacheManager.Instance.Get<List<Playlist>>("Playlists");
}
else
{
var p = await _apiService.GetPlaylists();
playlists = p.ToList();
SingletonCacheManager.Instance.Add<List<Playlist>>(playlists, "Playlists", 20);
}
這樣的事情可能嗎:
List<Map> maps = this.CacheHelper.GetCachedItems<Map>(key, lengthoftime);
然后 GetCachedItems 將檢查緩存的項目並相應地檢索。 這似乎可以,但是當緩存的項目不存在時,我必須從 api/數據庫中檢索項目,我不知道是否可以使其通用。
唯一的解決方案是在傳入的類型上使用 switch 語句?
switch(<T>type)
{
case<Map>:
return _mapRepository.FindBy(x => x.Active);
case<Playlist>:
return await _apiService.GetPlaylists();
}
謝謝你的幫助。
我對此的解決方案是傳入獲取您需要緩存為 lambda 表達式的數據的函數。 這樣,緩存方法可以檢查緩存並僅在需要時調用委托。 例如:
public T Get<T>(string key, Func<T> getItemDelegate, int duration) where T : class
{
var cache = GetCache();
var item = SingletonCacheManager.Instance.Get<ListMap>>(key) as T;
if (item != null) return item;
item = getItemDelegate();
SingletonCacheManager.Instance.Add<T>(item, key, duration);
return item;
}
現在您可以像這樣一般地調用 Get 函數:
var maps = Get<List<Map>>(
"Maps",
() => _mapRepository.FindBy(x => x.Active).ToList(),
20);
你也可以這樣做:
public interface ICacheManager
{
IList<T> Get<T>(string name);
void Add<T>(IList<T> data, string Id, int lifeTime);
}
public class CacheHelper
{
private readonly Dictionary<Tuple<Type, string>, Func<IEnumerable<object>>> dataRetrievalFuncs;
private readonly ICacheManager cacheManager;
public CacheHelper(ICacheManager cacheManager)
{
this.cacheManager = cacheManager;
dataRetrievalFuncs = new Dictionary<Tuple<Type, string>, Func<IEnumerable<object>>>();
}
public void Register<T>(string name, Func<IEnumerable<T>> selector) where T : class
{
dataRetrievalFuncs[new Tuple<Type, string>(typeof(T), name)] =
() => (IEnumerable<object>)selector();
}
public IList<T> GetCachedItems<T>(string name, int lifeTime = 20)
where T : class
{
var data = cacheManager?.Get<T>(name);
if (data == null)
{
data = (dataRetrievalFuncs[new Tuple<Type, string>(
typeof(T), name)]() as IEnumerable<T>)
.ToList();
cacheManager.Add(data, name, lifeTime);
}
return data;
}
}
現在,您需要為每種類型注冊數據檢索函數,然后只需使用幫助程序:
//Setting up the helper
CacheHelper helper = new CacheHelper(SingletonCacheManager.Instance);
helper.Register("Maps", () => _mapRepository.FindBy(x => x.Active));
helper.Register( "PlayLists", ... );
//Retrieving data (where it comes from is not your concern)
helper.GetCachedItems<Map>("Maps");
helper.GetCachedItems<PlayList>("Playlists");
正如在下面的評論中指出的那樣,此解決方案可能存在用於檢索數據的依賴項 ( _mapRepository
) 的生命周期問題。 一種解決方法是使用相同的解決方案,但在數據檢索時明確傳遞依賴項:
public class CacheHelper
{
private readonly Dictionary<Tuple<Type, string>, Func<object, IEnumerable<object>>> dataRetrievalFuncs;
private readonly ICacheManager cacheManager;
public CacheHelper(ICacheManager cacheManager)
{
this.cacheManager = cacheManager;
dataRetrievalFuncs = new Dictionary<Tuple<Type, string>, Func<object, IEnumerable<object>>>();
}
public void Register<TEntity, TProvider>(string name, Func<TProvider, IEnumerable<TEntity>> selector)
where TEntity : class
where TProvider: class
{
dataRetrievalFuncs[new Tuple<Type, string>(typeof(TEntity), name)] =
provider => (IEnumerable<object>)selector((TProvider)provider)
}
public IList<TEntity> GetCachedItems<TEntity>(string name, object provider, int lifeTime = 20)
where TEntity : class
{
var data = cacheManager?.Get<TEntity>(name);
if (data == null)
{
data = (dataRetrievalFuncs[new Tuple<Type, string>(
typeof(TEntity), name)](provider) as IEnumerable<TEntity>)
.ToList();
cacheManager?.Add(data, name, lifeTime);
}
return data;
}
}
現在使用會略有不同:
//Setting up the helper
CacheHelper helper = new CacheHelper(SingletonCacheManager.Instance);
helper.Register("Maps", (MapRepository r) => r.FindBy(x => x.Active));
//Retrieving data (where it comes from is not your concern)
helper.GetCachedItems<Map>("Maps", _mapRepository);
請注意,最后一個解決方案不是類型安全的。 您可以將錯誤類型的provider
傳遞給GetCachedItems<T>
,這很不幸。
為什么不為地圖和播放列表使用不同的緩存? 如果這樣做,您可以編寫一個基本抽象類,並僅覆蓋每個從 api 讀取數據的方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.