Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Making Cache Aware ResolveUrl/Url.Content

DZone's Guide to

Making Cache Aware ResolveUrl/Url.Content

· Performance Zone
Free Resource

Evolve your approach to Application Performance Monitoring by adopting five best practices that are outlined and explored in this e-book, brought to you in partnership with BMC.

   Introduction:

  Caching static contents is great and recommended but in the same time we need to also consider the fact that the static contents can be updated any time. A popular way to handle this scenario is to change the contents path or append a query-string in contents url. But doing this manually each time whenever you update the file is not very easy. Recently, I faced the same issue on an application which was using caching heavily for static contents. I was able to automate the process of appending query-string in static contents url in an efficient way. In this article, I will show you how I was able to automate the process by creating cache aware ResolveUrl and Url.Content methods.


  Description:

  Before using and testing the cache aware ResolveUrl and Url.Content methods, I will recommend to configure IIS to add cache for static contents. Next just add these cache aware ResolveUrl and Url.Content extension methods in your application,

    
public static class UrlHelperExtensions
{
    public static string CacheAwareContent(this UrlHelper url, string contentPath, bool useLastModifiedDate = true)
    {
        var path = url.Content(contentPath);
        string v;
        if (useLastModifiedDate)
            v = GetFileLastModifiedDate(url.RequestContext.HttpContext, contentPath);
        else
            v = MD5HashFile(contentPath);
        return path + "?v=" + v;
    }
    private static string MD5HashFile(string contentPath)
    {
        byte[] hash = MD5.Create().ComputeHash(File.ReadAllBytes(contentPath));
        return BitConverter.ToString(hash).Replace("-", "");
    }
    private static string GetFileLastModifiedDate(HttpContextBase context, string contentPath)
    {
        var physicalPath = context.Server.MapPath(contentPath);
        string lastModifiedDate;
        if (context.Cache[physicalPath] == null)
        {
            lastModifiedDate = new FileInfo(physicalPath).LastWriteTime.ToString("yyyyMMddhhmmss");
            context.Cache.Insert(physicalPath, lastModifiedDate, new CacheDependency(physicalPath));
        }
        else
        {
            lastModifiedDate = context.Cache[physicalPath].ToString();
        }
        return lastModifiedDate;
    }
}
public static class PageExtensions
{
    public static string CacheAwareResolveUrl(this Page page, string relativeUrl, bool useLastModifiedDate = true)
    {
        var path = page.ResolveUrl(relativeUrl);
        string v;
        if (useLastModifiedDate)
            v = GetFileLastModifiedDate(page.ModelBindingExecutionContext.HttpContext, relativeUrl);
        else
            v = MD5HashFile(relativeUrl);
        return path + "?d=" + v;
    }
    private static string MD5HashFile(string relativeUrl)
    {
        byte[] hash = MD5.Create().ComputeHash(File.ReadAllBytes(relativeUrl));
        return BitConverter.ToString(hash).Replace("-", "");
    }
    private static string GetFileLastModifiedDate(HttpContextBase context, string relativeUrl)
    {
        var physicalPath = context.Server.MapPath(relativeUrl);
        string lastModifiedDate;
        if (context.Cache[physicalPath] == null)
        {
            lastModifiedDate = new FileInfo(physicalPath).LastWriteTime.ToString("yyyyMMddhhmmss");
            context.Cache.Insert(physicalPath, lastModifiedDate, new CacheDependency(physicalPath));
        }
        else
        {
            lastModifiedDate = context.Cache[physicalPath].ToString();
        }
        return lastModifiedDate;
    }
}

In the above code, I have added CacheAwareResolveUrl and CacheAwareContent extension methods. These methods are same as their ResolveUrl and Url.Content counterparts except that these methods append a new v query-string when required.

When required? Means whenever the static resource referred in the url change.

What we do when change happen? We will append resource's last modified date(or resource file hash) in the v query-string.

There will be a performance hit for every call of CacheAwareResolveUrl and CacheAwareContent? No because these extension methods leverages ASP.NET cache object.

Then how ASP.NET cache knows about the static resource change? Simple, using file dependency option of cache.

  Now you can easily use these extension methods in your page(or view), utilizing full static resource caching without fearing about the file change,

@Url.CacheAwareContent("~/Content/Site.css")
<%= this.CacheAwareResolveUrl("~/Content/Site.css")  %>

  Make sure to import the namespace in your web page(or view) before using these methods.


  Summary:


 In this article, I showed you how simply we can create cache aware ResolveUrl and Url.Content methods. Using these methods we can utilize full static caching without fearing about file update. Hopefully you will enjoy my this article too.

Learn tips and best practices for optimizing your capacity management strategy with the Market Guide for Capacity Management, brought to you in partnership with BMC.

Topics:

Published at DZone with permission of Imran Baloch, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}