Caching ASP.NET Web API with CacheCow

7.8k views Asked by At

I am trying to implement caching using CacheCow. I have two problems:

  1. In some cases I need to invalidate manually the cache of some resources.

    For example, I have a resource that it is called purchase, and other that is called pointMovements. They are not totally connected, but doing a post in purchase, implies some changes in pointMovement. Cachecow is not detecting these changes because I am not calling the API of pointmovements. So when I call the endpoint of pointmovements, the values are cached and I cannot get the new values.

    To solve this, I need to invalidate that manually, how is that possible?

  2. There are some controllers that I don't want to cache. I am trying to use attributes for doing that but it is not working. I am following this article but the attributes are ignored.

    How can I specify which controllers to cache?

3

There are 3 answers

0
Tri Q Tran On

I came across the same set of problems and found a solution for problem 2 (disable caching regardless of the default settings).

// This forces the server to not provide any caching by refreshing its cache table immediately (0 sec)
[HttpCacheRefreshPolicy(0)]
// This forces the client (browser) to not cache any data returned from the server (even if ETag is present) by setting the time-out to 0 and no-cache to true.
[HttpCacheControlPolicy(true, 0, true)]
public void MyController : ApiControler {... }

The attributes must be applied together for this to work. You can also control the caching at the action level by providing the same rules to each action.

I've still to figure out the solution for problem 1. but watch this space for updates.

Update I have found a solution to problem 1.

  1. Register the CachingHandler with your IoC container (in my case it's IUnityContainer)
  2. Inject the ICachingHandler into your Web API controller.
  3. To invalidate the resource, use ICachingHandler.InvalidateResource(HttpRequestMessage)

Please see a code example below. The solution has been tested.

public class Bootstrapper
{
    //...

    // Create a new caching handler and register it with the container.
    public void RegisterCache(HttpConfiguration config, IUnityContainer container)
    {
        var cachingHandler = new CachingHandler(config);
        // ...

        container.RegisterInstance<ICachingHandler>(cachingHandler);
    }
}

public class ResourceContoller : ApiController
{
    private ICachingHandler _cachingHandler;

    public ResourceContoller(ICachingHandler cachingHandler)
    {
        _cachingHandler = cachingHandler;       
    }

    [HttpPost]
    public void DeleteResource(int resourceId)
    {
        // Do the delete
        // ...

        // Now invalidate the related resource cache entry      
        // Construct a http request message to the related resource
        // HINT: The "DefaultApi" may not be your api route name, so change this to match your route.
        // GOTCHA: The route matching mechanism is case sensitive, so be aware!
        var relatedResource = new HttpRequestMessage(HttpMethod.Get, Url.Link("DefaultApi", new {controller = "linkedresource", action = "getlinkedresource", id: resourceId}));

        // Invalidate the resource with the caching handler.
        _cachingHandler.InvalidateResource(relatedResource);
    }
}
0
Ravi On

I solved your #1 question using below code. Here I extend the IRoutePatternProvider interface. Remember, what you return in GetRoutePattern should match what you return in GetLinkedRoutePatterns. Only then the adding and removing will work. Try it out.

Inside Application_Start

CachingHandler cacheHandler = new CachingHandler(GlobalConfiguration.Configuration);
        cacheHandler.RoutePatternProvider = new CacheRoutePatternProvider();
        GlobalConfiguration.Configuration.MessageHandlers.Add(cacheHandler);

Custom Class

public class CacheRoutePatternProvider : IRoutePatternProvider
{
    public string GetRoutePattern(HttpRequestMessage request)
    {
        string path = request.RequestUri.AbsolutePath;
        if (!path.EndsWith("/"))
            path += "/";

        return path;
    }

    public IEnumerable<string> GetLinkedRoutePatterns(HttpRequestMessage request)
    {
        string path = request.RequestUri.AbsolutePath;
        if(!path.EndsWith("/"))
            path += "/";

        int segmentIndex;
        // return each segment of the resource heirarchy
        while ((segmentIndex = path.LastIndexOf("/")) > 0)
        {
            path = path.Substring(0, segmentIndex);

            if(path.Contains("/api/"))
                yield return path + "/";
        }

        yield break;
    }
}
0
Aliostad On

Sorry for the late response.

As @Tri Q said, the way to do this is to use attributes which I have explained in this blog:

http://byterot.blogspot.co.uk/2013/03/rest-asp-net-wep-api-0.4-new-features-breaking-change-cachecow-server.html