获取集合的缓存模式

时间:2016-12-22 10:28:44

标签: c# performance caching design-patterns async-await

是否有可用于获取多个项目的缓存策略/模式,其中只有一些项目可以缓存?在这种情况下使用缓存是否有意义?

更多详情

缓存将返回单个(或已知数量)结果缓存的简单;如果它在缓存中我们返回它,如果不是我们获取它,将它添加到缓存中,然后返回它:

//using System.Runtime.Caching;
ObjectCache cache = MemoryCache.Default;
TimeSpan cacheLifetime = new TimeSpan(0, 20, 0);
SomeObjectFinder source = new SomeObjectFinder();
public SomeObject GetById(long id)
{
    return GetFromCacheById(id) ?? GetFromSourceById(id);
}
protected SomeObject GetFromCacheById(long id)
{
    return (SomeObject)cache.Get(id.ToString(),null);
}
protected SomeObject GetFromSourceById (long id)
{
    SomeObject result = source.GetById(id);
    return result == null ? null : (SomeObject)cache.AddOrGetExisting(id.ToString(), result, DateTimeOffset.UtcNow.Add(cacheLifetime), null);
}

但是,如果我们不知道会有多少结果,如果我们从缓存中获取结果,我们就不会知道我们已经获取了所有内容;只有缓存的内容。因此,按照上述模式,如果没有或所有结果都被缓存,我们就没事了;但如果他们中有一半是,我们会得到一个部分结果集。

我认为下面的内容可能有意义;但我以前没有使用过async / await,我读过的大部分内容都暗示从同步代码调用异步代码通常被认为是错误的;所以这个解决方案可能不是一个好主意。

public IEnumerable<SomeObject> GetByPartialName(string startOfName)
{
    //kick off call to the DB (or whatever) to get the full result set 
    var getResultsTask = Task.Run<IList<SomeObject>>(async() => await GetFromSourceByPartialNameAsync(startOfName));
    //whilst we leave that to run in the background, start getting and returning the results from the cache
    var cacheResults = GetFromCacheByPartialName(startOfName);
    foreach (var result in cacheResults)
    {
        yield return result;
    }
    //once all cached values are returned, wait for the async task to complete, remove the results we'd already returned from cache, then add the remaining results to cache and return those also
    var results = getResultsTask.GetAwaiter().GetResult();
    foreach (var result in results.Except(cacheResults))
    {
        yield return CacheAddOrGetExistingByName(result.Name, result);
    }
}
protected async Task<IList<SomeObject>> GetFromSourceByPartialNameAsync(string startOfName)
{
    return source.GetByPartialName(startOfName);
}

我的假设是答案是“在这种情况下要么事先缓存所有内容,要么不使用缓存”......但希望有更好的选择。

1 个答案:

答案 0 :(得分:1)

您可以使用装饰器和策略模式,这里是Steve Smith post regarding building a cached repository的链接,并且对于单个项目也可以工作,因此您可以使用redis使用装饰器,如果没有找到它会进入数据库然后将它保存到数据库之后,查询数据将非常快。

以下是一些示例代码:

public class CachedAlbumRepository : IAlbumRepository
{
    private readonly IAlbumRepository _albumRepository;

public CachedAlbumRepository(IAlbumRepository albumRepository)
{
    _albumRepository = albumRepository;
}

private static readonly object CacheLockObject = new object();

public IEnumerable<Album> GetTopSellingAlbums(int count)
{
    Debug.Print("CachedAlbumRepository:GetTopSellingAlbums");
    string cacheKey = "TopSellingAlbums-" + count;
    var result = HttpRuntime.Cache[cacheKey] as List<Album>;
    if (result == null)
    {
        lock (CacheLockObject)
        {
            result = HttpRuntime.Cache[cacheKey] as List<Album>;
    if (result == null)
            {
                result = _albumRepository.GetTopSellingAlbums(count).ToList();
                HttpRuntime.Cache.Insert(cacheKey, result, null, 
                    DateTime.Now.AddSeconds(60), TimeSpan.Zero);
            }
        }
    }
    return result;
}
}