【发布时间】:2018-07-22 23:02:57
【问题描述】:
我正在寻找一种方法来包装一个为某些数据调用远程 WCF 服务的服务。例如,此类服务可能如下所示
public interface IAsyncSvc
{
Task<int[]> GetData(int key);
}
public class SomeAsyncSvc:IAsyncSvc
{
public Task<int[]> GetData(int key)
{
Console.WriteLine("SomeAsyncSvc::GetData()");
return Task.Factory.StartNew(() =>
{
//Some time-consuming operation
Thread.Sleep(1000);
return Enumerable.Range(1, key).ToArray();
});
}
}
我第一次写了一个简单的缓存包装器:
public class SomeAsyncSvcCachedTask : IAsyncSvcCached
{
private readonly IAsyncSvc _svc;
private readonly IMemoryCache _cache;
public SomeAsyncSvcCachedTask(IAsyncSvc svc, IMemoryCache cache)
{
_svc = svc;
_cache = cache;
}
public Task<int[]> GetData(int v)
{
if (_cache.TryGetValue(v, out Task<int[]> cacheEntry))
return cacheEntry;
var task = _svc.GetData(v);
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromSeconds(5));
_cache.Set(v, task, cacheEntryOptions);
return task;
}
}
缓存Task的主要缺点是,如果第一次尝试缓存失败,我会缓存一个出错的任务,并一遍又一遍地查询Task.Result,直到另一个未缓存的成功调用。
然后我为它写了另一个包装器:
public class SomeAsyncSvcCached : IAsyncSvcCached
{
private readonly IAsyncSvc _svc;
private readonly IMemoryCache _cache;
public SomeAsyncSvcCached(IAsyncSvc svc, IMemoryCache cache)
{
_svc = svc;
_cache = cache;
}
public Task<int[]> GetData(int v)
{
if (_cache.TryGetValue(v, out int[] cacheEntry))
return Task.FromResult(cacheEntry);
var task = _svc.GetData(v);
task.ContinueWith(t =>
{
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromSeconds(5));
_cache.Set(v, t.Result, cacheEntryOptions);
}, TaskContinuationOptions.NotOnFaulted);
return task;
}
}
主要思想不是缓存Task<int[]>,而只是缓存int[]类型的结果。作为一个优势,如果第一次调用失败,那么这个包装器会尝试一遍又一遍地读取数据,而不是返回缓存的错误。
这种方法有什么缺陷?也许有一种更简单的方法来实现缓存返回Task<>的方法调用的目标?
【问题讨论】:
标签: c# .net asynchronous caching