[EN] [ASP.NET MVC] Custom output cache provider

September 13, 2011 -

Since ASP.NET 4 was released it’s possible to write custom output cache providers to change the way data are cached. For example, one of my customer wanted that some actions were cached on the file system to avoid loss of cache during an application pool reboot.

Be careful by using the following code because in many scenarios it can’t be used “as it is” (in a web farm, for example).

A custom output cache provider is a simple class that inherits from OutputCacheProvider and overrides these methods :

  • Add : adds an entry in the cache
  • Get : gets an entry from the cache
  • Remove : removes an entry from the cache
  • Set : updates an entry in the cache

Cached items must be serializable. In this sample, the following class will be used :

[Serializable]
public class CachedItem
{
    public object Item { get; set; }
    public DateTime UtcExpiry { get; set; }
}

In order to save an item on the file system the two following methods will be used. The first allows to get a file path from the cache key (generated by asp.net) and the second allows to save a CachedItem on the disk :

private string GetFilePathFromKey(string key)
{
    foreach (var invalidChar in Path.GetInvalidFileNameChars())
        key = key.Replace(invalidChar, '_');

    return Path.Combine(CacheDirectory, key);
}

private void SaveCachedItem(CachedItem cachedItem, string filePath)
{
    if (File.Exists(filePath))
        File.Delete(filePath);

    using (var stream = File.OpenWrite(filePath))
    {
        var binaryFormatter = new BinaryFormatter();
        binaryFormatter.Serialize(stream, cachedItem);
    }
}

The Add method :

public override object Add(string key, object entry, DateTime utcExpiry)
{
    string filePath = GetFilePathFromKey(key);
    var cachedItem = GetCachedItem(filePath);

    if (cachedItem != null && cachedItem.UtcExpiry.ToUniversalTime() <= DateTime.UtcNow)
    {
        Remove(key);
    }
    else if (cachedItem != null)
    {
        return cachedItem.Item;
    }

    SaveCachedItem(new CachedItem()
    {
        Item = entry,
        UtcExpiry = utcExpiry
    }, filePath);

    return entry;
}

The Get method :

public override object Get(string key)
{
    string filePath = GetFilePathFromKey(key);
    var cachedItem = GetCachedItem(filePath);
    if (cachedItem != null)
    {
        if (cachedItem.UtcExpiry.ToUniversalTime() <= DateTime.UtcNow)
        {
            Remove(key);
        }
        else
        {
            return cachedItem.Item;
        }
    }

    return null;
}

The Remove method :

public override void Remove(string key)
{
    string filePath = GetFilePathFromKey(key);
    if (File.Exists(filePath))
        File.Delete(filePath);
}

The Set Method :

public override void Set(string key, object entry, DateTime utcExpiry) {
    string filePath = GetFilePathFromKey(key);
    var cachedItem = new CachedItem() {Item = entry, UtcExpiry = utcExpiry};
    SaveCachedItem(cachedItem, filePath);
}

Now that the output cache provider is created, it can be registered in the web.config file of the application, in the caching section :

<system.web>
<caching>
  <outputCache>
    <providers>
      <add name="FileSystemOutputCacheProvider"
           type="Samples.FileSystemOutputCacheProvider, Samples"/>
    </providers>
  </outputCache>
</caching>

The last step consists to override the GetOutputCacheProviderName method in the Global.asax file. Actually this method is called by the ASP.NET Framework when an OutputCache attribute (or an OutputCache directive) is used. The method returns the name of the output cache provider to use in the current context. For example, if you want to use the FileSystemOutputCacheProvider for the Details action of the Products controller, you can use this code :

public override string GetOutputCacheProviderName(HttpContext context)
{
    if (context.Request.RawUrl.ToUpper().Contains("PRODUCTS/DETAILS")) {
        return "FileSystemOutputCacheProvider";
    }
    return base.GetOutputCacheProviderName(context);
}

As mentioned previously you just have to set an OutputCache attribute on the action Details of the Products controller to use the custom output cache provider :

public class ProductsController : Controller
{
    [OutputCache(Duration = 3600, VaryByParam = "id")]
    public ActionResult Details(int id)
    {
        //long operation
        Thread.Sleep(2000);

        ViewBag.ProductId = id;
        return View();
    }
}

Download the source code here.

Hope this helps Winking smile

Comments

Share

Tags


Twitter


Hyper-V #Containers in #Windows 10 - https://t.co/ozT1MGQJkm

May 27, 2016 15:56

#DevOps: Continuous deployment/delivery with #Jenkins and @VisualStudio Team Services https://t.co/dtVnoHJ44W

May 27, 2016 15:55

Wow! #Microsoft and #Facebook to build subsea cable across Atlantic - https://t.co/EOpi4dCQoR

May 27, 2016 15:53

#Docker Containers as the new Binaries of deployment - https://t.co/jjYx4SsbLj

May 26, 2016 16:59

Turning on #Windows features using #Powershell DSC extension and #Azure CLI - https://t.co/kxpnBMVXJj

May 26, 2016 16:57

RT @meulta: Full packed lab about #DevOps and @VorlonJS in DevOps intensive track #msdevcon Russia :) cc @jcorioland https://t.co/tH6W4DBaPE

May 26, 2016 14:06

RT @a_bogdanova: Ключевой доклад офисного трека на #msdevcon уже начался! https://t.co/TV4iebh3LX

May 26, 2016 07:36

The future of mobile app development: learn, build, integrate, test, iterate. https://t.co/Puy8hcZGBe #DevOps #Xamarin #Azure #VisualStudio

May 26, 2016 07:33

#Docker All The Things at #OSCON! https://t.co/LrblXcNO6r

May 26, 2016 07:22

Creating Better User Experiences with #Microsoft #CognitiveServices https://t.co/Uf2hVkvX4Y

May 26, 2016 07:18

Announcing #WCF Connected Service for .NET Core RC2 and https://t.co/7Z4RnrQUHN Core RC2 https://t.co/psjl3rKI2I #dotnet #dotnetcore

May 26, 2016 07:10

Announcing general availability of @Azure #DevTest Labs https://t.co/V1gOTKmmCW

May 26, 2016 05:36

#DevOps mobile demonstration using #Xamarin Test Cloud and @VisualStudio Team Services! #msdevcon https://t.co/unWRCWfitR

May 25, 2016 07:56

#OSS and #Linux are first-class citizen in #Microsoft @Azure! #msdevcon https://t.co/VU8k0OK3ph

May 25, 2016 07:50