簡體   English   中英

C#中的通用緩存

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM