我正在寻找一种方法来包装一个为某些数据调用远程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;
}
}
缓存任务&lt;&gt;的主要缺点如果第一次尝试缓存失败,我会缓存一个错误的任务并一遍又一遍地接收相同的异常查询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<>
的缓存方法调用的目标?
答案 0 :(得分:0)
看一下这篇文章: http://cpratt.co/thread-safe-strongly-typed-memory-caching-c-sharp/
public static async Task<T> AddOrGetExistingAsync<T>(
this ObjectCache cache,
string key,
Func<Task<T>> valueFactory,
CacheItemPolicy policy)
{
var newValue = new AsyncLazy<T>(valueFactory);
var oldValue = cache.AddOrGetExisting(key, newValue, policy) as Lazy<T>;
try
{
return oldValue != null ? oldValue.Value : await newValue.Value;
}
catch
{
cache.Remove(key);
throw;
}
}