Caching Values Inside HttpResponseMessage with Polly – caching series 2/3

Full source code here.

In this, the second of three posts on caching in Polly, I will show how to cache the values returned inside a HttpResponseMessage as opposed to caching the response with all its various elements.

The first post explained how to selectively cache a response, based on the Http StatusCode. The next post will show how to combine these two ideas and selectively cache the values inside the HttpResponseMessage.

Caching the whole response or the value
Caching the whole HttpResponseMessage is useful if your business logic depends on interrogating the response for values outside the main payload or you want a quick and easy caching solution.

If on the other hand, you only need the payload, there is no need to cache the whole response.

Both cases are plausible, and it is up to do decide which is more appropriate for your application.

How to cache the value inside HttpResponseMessage
The first step is to set the cache inside ConfigureServices.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMemoryCache();
    services.AddSingleton<IAsyncCacheProvider, MemoryCacheProvider>();

Then in Configure, setup the policy to store the type you expect to find in the response. In this example it is in a int, but it could easily be a complex layered model representing anything.

public void Configure(IApplicationBuilder app, IHostingEnvironment env,
    IAsyncCacheProvider cacheProvider, IPolicyRegistry<string> registry)
{
    CachePolicy<int> cachePolicy =
        Policy.CacheAsync<int>(cacheProvider, TimeSpan.FromSeconds(10));

    registry.Add("CachingPolicy", cachePolicy);

Note, how I specified an int rather than a HttpResponseMessage.

In the previous post, I used the HttpClientFactory to select the policy from a Polly policy registry. I could do that because the type I was storing was HttpResponseMessage, that is not the case now and HttpClientFactory will not play nice when trying to cache anything other than a HttpResponseMessage. Instead I’m going to pass the policy registry directly to the controller and get the policy from this, I am still using the HttpClientFactory to pass HttpClient, but not for policy selection.

Here is how the controller starts.

public class CatalogController : Controller
{
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly IPolicyRegistry<string> _policyRegistry;

    public CatalogController(IHttpClientFactory httpClientFactory, IPolicyRegistry<string> policyRegistry)
    {
        _httpClientFactory = httpClientFactory;
        _policyRegistry = policyRegistry;
    }

And here is the GET method.

[HttpGet("{id}")]
public async Task<IActionResult> Get(int id)
{
    string requestEndpoint = $"inventory/{id}";
    var httpClient = _httpClientFactory.CreateClient("RemoteServer");

    var cachePolicy = _policyRegistry.Get<CachePolicy<int>>("CachingPolicy");
    Context policyExecutionContext = new Context($"GetInventoryById-{id}");

    int itemsInStock =
        await cachePolicy.ExecuteAsync(context => GetItemsInStockCount(), policyExecutionContext);

    async Task<int> GetItemsInStockCount()
    {
        HttpResponseMessage response = await httpClient.GetAsync(requestEndpoint);

        if (response.IsSuccessStatusCode)
        {
            int itemsInStockCount = await response.Content.ReadAsAsync<int>();
            return itemsInStockCount;
        }

        return 0;
    }

    return Ok(itemsInStock);
}

The first few lines are straight forward, setup the endpoint, get the HttpClient, get the cache policy. Like in the previous post, I use the context when placing items into the cache and retrieving them.

Because I want to cache the value inside the HttpResponse (and not the response itself) I can’t call the HttpClient from inside the policy’s exec method. Instead I need to call something that returns the int from inside the HttpResponse.

The easiest way of doing this is to use a local function which makes the HttpClient request, deserializes the response and returns the int from the response.
In this post I am assuming that all values inside the response should be cached, I am NOT checking the status code of the response.

The policy then caches the int for the period specified in Startup.

That’s all there is to it.

If you are wondering how to selectively cache values inside the HttpResponse, tune in for the next post in this series.

Full source code here.

Using the In-Memory Cache with .NET Core Web API

Full source code available here.

If you need to store anything temporarily within an application, you should consider using the In-Memory Cache from Microsoft. In .NET Core it is much easier to use than its predecessor from Framework 4.x. You can store almost anything in it, in a type safe manner.

When adding an item to the cache you set the an expiration, this can be an absolute time, a sliding window or the combination of both. Choosing which one to use depends on your scenario.

When an item is removed from the cache a delegate can be called, you can use this for logging or some other purpose.
Items can also be evicted when the cache is running low on memory.

In this example I’m going to show a simple usage example for the cache inside a .NET Core Web API application.
Just a single line in startup.cs adds the cache to the dependency injection container.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMemoryCache();
    services.AddMvc();
}

Now I can access the cache from inside all my controllers by just adding an IMemoryCache parameter to the constructor.

private readonly IMemoryCache _cache;
public CountriesController(IMemoryCache memoryCache)
{
    _cache = memoryCache;
}

I have a very simple GET method that takes a country code as its parameter and looks up full name of the country.

[HttpGet("{countryCode}")]
public async Task<IActionResult> Get(string countryCode)
{
    string country = await LookupCountryByCode(countryCode);

    if (string.IsNullOrEmpty(country))
    {
        return NoContent();
    }
    return Ok(country);
}

And here is where I use the cache.

private async Task<string> LookupCountryByCode(string countryCode)
{
    if(!_cache.TryGetValue("ListOfCountries", out Dictionary<string, string> countries))
    {
        Console.WriteLine("Cache miss....loading from database into cache");
        countries =
            JsonConvert.DeserializeObject<Dictionary<string, string>>(
                await System.IO.File.ReadAllTextAsync("countrylist.json"));

        _cache.Set("ListOfCountries", countries, TimeSpan.FromSeconds(10));
    }
    else
    {
        Console.WriteLine("Cache hit");
    }

    return countries[countryCode];
}

On line 3, I check if the list of countries is in the cache; I won’t be the first time through this method or if the cache entry has expired.
Inside the if clause I load the list of countries from a json file, but you could load from a database or any other source. Then I add the list to the cache and set when the entry will expire, in this example it will expire 10 seconds after it was added.

If you want to use both an absolute expiration and sliding window you need to pass the MemoryCacheEntryOptions when adding an item to the cache.

MemoryCacheEntryOptions options = new MemoryCacheEntryOptions
{
    AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(25), // cache will expire in 25 seconds
    SlidingExpiration = TimeSpan.FromSeconds(5) // caceh will expire if inactive for 5 seconds
};

_cache.Set("ListOfCountries", countries, options);

Full source code available here.