Pal*_*ris 5 .net c# caching asynchronous
我正在研究一种包装调用远程 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();
});
}
}
Run Code Online (Sandbox Code Playgroud)
我第一次编写了一个简单的缓存包装器:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
缓存 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;
}
}
Run Code Online (Sandbox Code Playgroud)
主要思想不是缓存Task<int[]>,而只是缓存类型的结果int[]。一个优点是,如果第一次调用失败,那么此包装器将尝试一遍又一遍地读取数据,而不是返回缓存的错误。
这种方法有什么缺陷?也许有一种更简单的方法来实现缓存返回的方法调用的目标Task<>?
看看这篇文章: 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;
}
}
Run Code Online (Sandbox Code Playgroud)