[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


Parametrized Localization in ASPNET http://t.co/a075DrhZLB

August 18, 2014 16:28

Creating Custom #AngularJS Directives Part 3 - Isolate Scope and Function Parameters http://t.co/SVEVyLxHZh

August 18, 2014 15:16

Scripts-to-Tools: Auto-provisioning #Azure Virtual Networks with #PowerShell and XML - http://t.co/7BHjy0IdOT

August 16, 2014 08:37

@CloudExpo | Migrating Apps to the #Cloud - http://t.co/5UOtM6Oo6M

August 16, 2014 07:21

Renowned Security Expert Kevin Mitnick Can Steal Your Identity In Three Minutes - http://t.co/BJxwnRj1Yq

August 16, 2014 07:01

Building #Cloud Based Games – A look into how they did it with Wordament - http://t.co/MTy8wimXtx

August 15, 2014 16:28

August updates for Internet Explorer : http://t.co/Uzr9U9361q

August 15, 2014 15:16

Using Application Insights to aid in A/B Testing : http://t.co/EcW249b7KY

August 15, 2014 13:17

Internet of Things: Gadgeteers, Gatherers, Messengers, and Controllers : http://t.co/3WBcPtxxuE #IoT

August 15, 2014 12:01

Microsoft @Azure Offered "Best #Cloud Education" At @CloudExpo NY | Cloud Computing Journal http://t.co/MsuJEmlYRi

August 15, 2014 09:54

When Will You Touch The Internet of Things? - Forbes http://t.co/5vcfQK2ZPv #IoT

August 15, 2014 08:37

These 8 lines of code can make debugging your ASPNet Web API a little bit easier - http://t.co/uAvvZBXVsv

August 13, 2014 13:17

Centralized exception handling using a ASPNET Web API MessageHandler - http://t.co/tbeSqbibTw

August 13, 2014 12:01

Tests de charge avec Visual Studio Online dans #Azure (Cloud Cover Show 145) - http://t.co/Bfqt1HcUEy

August 13, 2014 09:54

#Azure #Storage - Essential Facts - http://t.co/georO7E5tZ

August 13, 2014 08:37