MVC 5 HttpContext.Current.Cache method fails when hit by 2 threads at the same time

275 Views Asked by At

I'm using the following service method in order to cache the result of a query:

    private readonly CoreDbContext _dbContext;

    public EcommerceProductService()
    {
        _dbContext = GetDbContext();
    }

    public IEnumerable<EcommerceProduct> GetAllCached()
    {
        var cachedResult = HttpContext.Current.Cache["EcommerceProductService.GetAllCached"] as IEnumerable<EcommerceProduct>;

        if (cachedResult == null)
        {
            var result = _dbContext.EcommerceProducts.ToList();

            HttpContext.Current.Cache.Insert("EcommerceProductService.GetAllCached", result);

            return result;
        }

        return cachedResult;
    }

In a certain page, this method is called simultaneously by 2 threads (because I need to display the entire collection of products twice, but with different filters).

Oddly, the first time I launch the application only one of the threads "wins" and receives the list of products, while the other receives null. If I refresh the page they both start to work fine (because at that point they fetch the result from the cache), but the first time it's only one or the other, they never both work.

I also tried to wrap the entire code in a Lock statement, but it didn't change a thing. What am I missing?

    private readonly CoreDbContext _dbContext;
    private static readonly object Locker = new object();

    public EcommerceProductService()
    {
        _dbContext = GetDbContext();
    }

    public IEnumerable<EcommerceProduct> GetAllCached()
    {
        lock (Locker)
        {
            var cachedResult = HttpContext.Current.Cache["EcommerceProductService.GetAllCached"] as IEnumerable<EcommerceProduct>;

            if (cachedResult == null)
            {
                var result = _dbContext.EcommerceProducts.ToList();

                HttpContext.Current.Cache.Insert("EcommerceProductService.GetAllCached", result);

                return result;
            }

            return cachedResult;
        }

    }
1

There are 1 best solutions below

5
kagelos On

Change your _locker declaration to

private static object _locker = new object();

As you have it now, each instance of EcommerceProductService assigns a new value to it, rendering locking useless.