c#中的通用缓存

时间:2017-02-01 14:33:02

标签: c# generics caching

是否有更有效的方法来检查缓存数据是否退出,是否确实获得了它,以及它是否未调用api /数据库然后缓存它?我一遍又一遍地执行这样的代码似乎效率很低。

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

感谢您的帮助。

3 个答案:

答案 0 :(得分:1)

我的解决方案是传递获取您需要缓存的数据的函数作为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);

答案 1 :(得分:1)

你也可以这样做:

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>,这是不幸的。

答案 2 :(得分:0)

为什么不为地图和播放列表使用不同的缓存?如果你这样做,你可以编写一个基本的抽象类,并且只覆盖从每个抽取api中读取数据的方法。