Я ищу элегантный способ кэширования результатов моих асинхронных операций.
Сначала у меня был синхронный метод:
public String GetStuff(String url)
{
WebRequest request = WebRequest.Create(url);
using (var response = request.GetResponse())
using (var sr = new StreamReader(response.GetResponseStream()))
return sr.ReadToEnd();
}
Затем я сделал асинхронным:
public async Task<String> GetStuffAsync(String url)
{
WebRequest request = WebRequest.Create(url);
using (var response = await request.GetResponseAsync())
using (var sr = new StreamReader(response.GetResponseStream()))
return await sr.ReadToEndAsync();
}
Затем я решил, что я должен кэшировать результаты, поэтому мне не нужно часто запрашивать вне этого:
ConcurrentDictionary<String, String> _cache = new ConcurrentDictionary<String, String>();
public async Task<String> GetStuffAsync(String url)
{
return _cache.GetOrAdd(url, await GetStuffInternalAsync(url));
}
private async Task<String> GetStuffInternalAsync(String url)
{
WebRequest request = WebRequest.Create(url);
using (var response = await request.GetResponseAsync())
using (var sr = new StreamReader(response.GetResponseStream()))
return await sr.ReadToEndAsync();
}
Затем я прочитал статью (смотрел видео) о том, как лучше кэшировать Task<T>
, потому что их создание дороговато:
ConcurrentDictionary<String, Task<String>> _cache = new ConcurrentDictionary<String, Task<String>>();
public Task<String> GetStuffAsync(String url)
{
return _cache.GetOrAdd(url, GetStuffInternalAsync(url));
}
private async Task<String> GetStuffInternalAsync(String url)
{
WebRequest request = WebRequest.Create(url);
using (var response = await request.GetResponseAsync())
using (var sr = new StreamReader(response.GetResponseStream()))
return await sr.ReadToEndAsync();
}
И теперь проблема заключается в том, что если запрос не сработает (например, HTTP 401), кэш будет содержать сбой Task<String>
, и мне придется использовать reset приложение, потому что будет невозможно отправить запрос повторно.
Есть ли элегантный способ использования ConcurrentDictionary<T1,T2>
для кэширования только успешных задач и по-прежнему иметь атомное поведение?