title: Web API Caching - How to implement Invalidation using a distributed cache
body: <p>I have an API that currently does not use any caching. I do have one piece of Middleware that I am using that generates cache headers (Cache-Control, Expires, ETag, Last-Modified - using the <a href="https://github.com/KevinDockx/HttpCacheHeaders" rel="nofollow noreferrer">https://github.com/KevinDockx/HttpCacheHeaders</a> library). It does not store anything as it only generates the headers. </p>
  
  <p>When an If-None-Match header is passed to the API request, the middleware checks the Etag value passed in vs the current generated value and if they match, sends a 304 not modified as the response (<code>httpContext.Response.StatusCode = StatusCodes.Status304NotModified;</code>)</p>
  
  <p>I'm using a Redis cache and I'm not sure how to implement cache invalidation. I used the <code>Microsoft.Extensions.Caching.Redis</code> package in my project. I installed Redis locally and used it in my controller as below:</p>
  
  <pre><code>[AllowAnonymous]
  [ProducesResponseType(200)]
  [Produces("application/json", "application/xml")]
  public async Task&lt;IActionResult&gt; GetEvents([FromQuery] ParameterModel model)
          {
              var cachedEvents = await _cache.GetStringAsync("events");
              IEnumerable&lt;Event&gt; events = null;
  
              if (!string.IsNullOrEmpty(cachedEvents))
              {
                  events = JsonConvert.DeserializeObject&lt;IEnumerable&lt;Event&gt;&gt;(cachedEvents);
              }
              else
              {
                  events = await _eventRepository.GetEventsAsync(model);
                  string item = JsonConvert.SerializeObject(events, new JsonSerializerSettings()
                  {
                      ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                  });
                  await _cache.SetStringAsync("events", item);
              }
  
              var eventsToReturn = _mapper.Map&lt;IEnumerable&lt;EventViewDto&gt;&gt;(events);
              return Ok(eventsToReturn);
          }
  </code></pre>
  
  <p>Note that <code>_cache</code> here is using <code>IDistributedCache</code>. This works as the second time the request is hitting the cache. But when the <code>Events</code> I am fetching are modified, it does not take the modified values into account. It serves up the same value without doing any validation.</p>
  
  <p>My middlware is setup as:
  Cache Header Middleware -> MVC. So the cache headers pipeline will first compare the Etag value sent by the client and either decides to forward the request to MVC or short circuits it with a 304 not modified response. </p>
  
  <p>My plan was to add a piece of middleware prior to the cache header one (i.e. My Middleware -> Cache Header Middleware -> MVC) and wait for a response back from the cache header middleware and check if the response is a 304. If 304, go to the cache and retrieve the response. Otherwise update the response in the cache. </p>
  
  <p>Is this the ideal way of doing cache invalidation? Is there a better way of doing it? With above method, I'll have to inspect each 304 response, determine the route, and have some sort of logic to verify what cache key to use. Not sure if that is the best approach. </p>
  
  <p>If you can provide some guidelines and documentation/tutorials on cache invalidation, that would be really helpful.</p>