Home Proper way to cache results Task<T> with IMemoryCache
Reply: 0

Proper way to cache results Task<T> with IMemoryCache

user6803
1#
user6803 Published in September 21, 2018, 8:05 am

I'm looking into a way to wrap a service that calls a remote WCF service for some data. For example, such service may look like this

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();
        });
    }
}

For the first time, I wrote a simple caching wrapper:

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;
    }
}

The main disadvatage of caching Task<> is that if a first attempt to cache failed, I would cache a faulted task and receive the same exception over and over again querying Task.Result until another non-cached successful call.

Then I wrote another wrapper for it:

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;
    }
}

The main idea is not to cache Task<int[]>, but only a result of it of type int[]. As an advantage, if a first call failed, then this wrapper would try to read data over and over again instead of returning cached faults.

What are the flaws of such approach? Maybe there is a simpler way to achieve goal of caching method calls which return Task<>?

share|improve this question

1 Answer 1

active oldest votes
up vote 0 down vote
You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.319452 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO