Tag Archive | mvc

Custom ASP.NET MVC Action Result Cache Attribute

If you’re working on an application built using ASP.NET MVC, you’re hopefully aware of the OutputCacheAttribute attribute which can be used to statically cache your dynamic web pages. By adding this attribute to a controller or action method, the output of the method(s) will be stored in memory. For example, if your action method renders a view, then the view page will be cached in memory. This cached view page is then available to the application for all subsequent requests (or until the item expires out of the cache), which can retrieve it from the memory rather than redoing the work to re-create the result again. This is the essence of caching: trading memory for performance.

The OutputCacheAttribute is a really powerful way to improve performance in your MVC application, but isn’t always the most practical. Because it caches the entire page as raw HTML, it circumvents a large part of the MVC pipeline and thus also skips the code that runs to generate the page. This means that if your view has dynamic content that comes from session or ViewData, such as displaying the currently logged in user’s name in the top bar, or the current time of day, or the resulting view of an invalid form post which tells your user to correct their input errors, you’ll quickly discover the error of your ways when you try to cache that page. When David accesses the logged in page for the first time and caches it, everybody else who logs in will be called David on the page. And if David fills out your empty form and presses submit, only to cache the resulting input validation error page, then everybody will see David’s completed form when they have errors too – maybe even including sensitive data like his username, password, or even his credit card information. I think that most of us have seen this kind of (often humorous) caching error before. It’s scary stuff, nonetheless.

A great way to balance the benefits of output caching with the dynamic content and features that the modern ASP.NET MVC web application offers is to create a custom caching attribute. This attribute can cache the ActionResult instead of the raw HTML of the page, and in doing so will allow you to cache all of the work that is done to generate the ActionResult (be it ViewResult or otherwise). By executing within the MVC pipeline, this custom caching attribute will not interrupt or short-circuit the MVC pipeline. This allows for things like SessionState or ViewData to vary per cached request! It’s not quite as efficient as the true OutputCacheAttribute, but my custom ActionResultCacheAttribute is an excellent tradeoff between performance and dynamic data:

/// <summary>
/// Caches the result of an action method.
/// NOTE: you'll need refs to System.Web.Mvc and System.Runtime.Caching
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class ActionResultCacheAttribute : ActionFilterAttribute
{
    private static readonly Dictionary<string, string[]> _varyByParamsSplitCache = new Dictionary<string, string[]>();
    private static readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
    private static readonly MemoryCache _cache = new MemoryCache("ActionResultCacheAttribute");

    /// <summary>
    /// The comma separated parameters to vary the caching by.
    /// </summary>
    public string VaryByParam { get; set; }

    /// <summary>
    /// The sliding expiration, in seconds.
    /// </summary>
    public int SlidingExpiration { get; set; }

    /// <summary>
    /// The duration to cache before expiration, in seconds.
    /// </summary>
    public int Duration { get; set; }

    /// <summary>
    /// Occurs when an action is executing.
    /// </summary>
    /// <param name="filterContext">The filter context.</param>
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // Create the cache key
        var cacheKey = CreateCacheKey(filterContext.RouteData.Values, filterContext.ActionParameters);

        // Try and get the action method result from cache
        var result = _cache.Get(cacheKey) as ActionResult;
        if (result != null)
        {
            // Set the result
            filterContext.Result = result;
            return;
        }

        // Store to HttpContext Items
        filterContext.HttpContext.Items["__actionresultcacheattribute_cachekey"] = cacheKey;
    }

    /// <summary>
    /// Occurs when an action has executed.
    /// </summary>
    /// <param name="filterContext">The filter context.</param>
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        // Don't cache errors
        if (filterContext.Exception != null)
        {
            return;
        }

        // Get the cache key from HttpContext Items
        var cacheKey = filterContext.HttpContext.Items["__actionresultcacheattribute_cachekey"] as string;
        if (string.IsNullOrWhiteSpace(cacheKey))
        {
            return;
        }

        // Cache the result of the action method
        if (SlidingExpiration != 0)
        {
            _cache.Add(cacheKey, filterContext.Result, TimeSpan.FromSeconds(SlidingExpiration));
            return;
        }

        if (Duration != 0)
        {
            _cache.Add(cacheKey, filterContext.Result, DateTime.UtcNow.AddSeconds(Duration));
            return;
        }

        // Default to 1 hour
        _cache.Add(cacheKey, filterContext.Result, DateTime.UtcNow.AddSeconds(60 * 60));
    }

    /// <summary>
    /// Creates the cache key.
    /// </summary>
    /// <param name="routeValues">The route values.</param>
    /// <returns>The cache key.</returns>
    private string CreateCacheKey(RouteValueDictionary routeValues, IDictionary<string, object> actionParameters)
    {
        // Create the cache key prefix as the controller and action method
        var sb = new StringBuilder(routeValues["controller"].ToString());
        sb.Append("_").Append(routeValues["action"].ToString());

        if (string.IsNullOrWhiteSpace(VaryByParam))
        {
            return sb.ToString();
        }

        // Append the cache key from the vary by parameters
        object varyByParamObject = null;
        string[] varyByParamsSplit = null;
        bool gotValue = false;

        _lock.EnterReadLock();
        try
        {
            gotValue = _varyByParamsSplitCache.TryGetValue(VaryByParam, out varyByParamsSplit);
        }
        finally
        {
            _lock.ExitReadLock();
        }

        if (!gotValue)
        {
            _lock.EnterWriteLock();
            try
            {
                varyByParamsSplit = VaryByParam.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
                _varyByParamsSplitCache[VaryByParam] = varyByParamsSplit;
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }

        foreach (var varyByParam in varyByParamsSplit)
        {
            // Skip invalid parameters
            if (!actionParameters.TryGetValue(varyByParam, out varyByParamObject))
            {
                continue;
            }

            // Sometimes a parameter will be null
            if (varyByParamObject == null)
            {
                continue;
            }

            sb.Append("_").Append(varyByParamObject.ToString());
        }

        return sb.ToString();
    }
}

You can use this method on a controller to affect all action methods:

[ActionResultCache(Duration = 60 * 60 * 24)]
public class HomeController : Controller
{
    public async Task<ActionResult> TermsOfService()
    {
        return View();
    }
}

Or just apply it to individual action methods:

[ActionResultCache(Duration = 60 * 60 * 24)]
public async Task<ActionResult> TermsOfService()
{
    return View();
}

You can also use it with the VaryByParam property to vary the cached result by the parameter(s) of the action method:

[ActionResultCache(Duration = 60 * 60 * 24, VaryByParam = "username")]
public async Task<ActionResult> ViewUser(string username)
{
    var model = new UserModel
    {
        Username = username,
        ...
    };

    return View(model);
}

The main benefit of this custom caching attribute is that your session state and all global action filter attributes, etc. still run in the MVC pipeline as they would normally. The only code cached and skipped over is the method body of the action method.

Please use and enjoy! Feedback welcomed in the comments.

Trigger IValidatableObject.Validate When ModelState.IsValid is false

I recently came across an ASP.NET MVC issue at work where the validation for my Model was not firing correctly. The Model implemented the IValidatableObject interface and thus the Validate method which did some specific logic to ensure the state of the Model (the ModelState). This Model also had some DataAnnotation attributes on it to validate basic input.

Long story short, the issue I encountered was that when ModelState.IsValid == false due to failure of the DataAnnotation validation, the IValidatableObject.Validate method is not fired, even though I needed it to be. This problem arose due to a rare situation in which ModeState.IsValid was initially false but was later set to true in the Controller’s Action Method by some logic that removed errors from the ModelState.

I did some research and learned that the DefaultModelBinder of ASP.NET MVC short-circuits it’s logic: if the ModelState is not valid (AKA is false), the IValidatableObject logic that runs the Validate method is never fired.

To thwart this, I created a custom Model Binder, a custom Model Binder Provider (to serve my custom Model Binder), and then registered the Model Binder Provider in the Application_Start method of Global.asax.cs. Here’s the code for a custom Model Binder that always fires the IValidatableObject.Validate method, even if ModelState.IsValid == false:

ForceValidationModelBinder:

/// <summary>
/// A custom model binder to force an IValidatableObject to execute the Validate method, even when the ModelState is not valid.
/// </summary>
public class ForceValidationModelBinder : DefaultModelBinder
{
    protected override void OnModelUpdated( ControllerContext controllerContext, ModelBindingContext bindingContext )
    {
        base.OnModelUpdated( controllerContext, bindingContext );

        ForceModelValidation( bindingContext );
    }

    private static void ForceModelValidation( ModelBindingContext bindingContext )
    {
        // Only run this code for an IValidatableObject model
        IValidatableObject model = bindingContext.Model as IValidatableObject;
        if( model == null )
        {
            // Nothing to do
            return;
        }

        // Get the model state
        ModelStateDictionary modelState = bindingContext.ModelState;

        // Get the errors
        IEnumerable<ValidationResult> errors = model.Validate( new ValidationContext( model, null, null ) );

        // Define the keys and values of the model state
        List<string> modelStateKeys = modelState.Keys.ToList();
        List<ModelState> modelStateValues = modelState.Values.ToList();

        foreach( ValidationResult error in errors )
        {
            // Account for errors that are not specific to a member name
            List<string> errorMemberNames = error.MemberNames.ToList();
            if( errorMemberNames.Count == 0 )
            {
                // Add empty string for errors that are not specific to a member name
                errorMemberNames.Add( string.Empty );
            }

            foreach( string memberName in errorMemberNames )
            {
                // Only add errors that haven't already been added.
                // (This can happen if the model's Validate(...) method is called more than once, which will happen when there are no property-level validation failures)
                int index = modelStateKeys.IndexOf( memberName );

                // Try and find an already existing error in the model state
                if( index == -1 || !modelStateValues[index].Errors.Any( i => i.ErrorMessage == error.ErrorMessage ) )
                {
                    // Add error
                    modelState.AddModelError( memberName, error.ErrorMessage );
                }
            }
        }
    }
}

ForceValidationModelBinderProvider:

/// <summary>
/// A custom model binder provider to provide a binder that forces an IValidatableObject to execute the Validate method, even when the ModelState is not valid.
/// </summary>
public class ForceValidationModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder( Type modelType )
    {
        return new ForceValidationModelBinder();
    }
}

Global.asax.cs:

protected void Application_Start()
{
    // Register the force validation model binder provider
    ModelBinderProviders.BinderProviders.Clear();
    ModelBinderProviders.BinderProviders.Add( new ForceValidationModelBinderProvider() );
}

MVC4 Conditional HTML Attributes

MVC4 made one simple and yet awesome improvement to View rendering that I don’t think many people are aware of.

Have you ever had to conditionally add an attribute to an HTML element in your MVC View based on the presence of a variable? The typical use case is applying a CSS class to a div. Most of the time that code looks something like this:

<div @(myClass == null ? "" : "class=\"" + myClass + "\"")></div>

What a pain – not only to write but to read… This destroys the View’s readability and clutters the HTML up big time!

In MVC4, this process is much simpler. Just do this instead:

<div class="@myClass"></div>

That’s all you need. If myClass is null, it will ignore the class attribute and render without the class:

<div></div>

If myClass is not null, it will apply the class to the div:

<div class="test"></div>

This should work with other HTML element attributes also, though I haven’t tested them all myself. What a great little improvement!

Presented at Code on the Beach

Today I presented to Code on the Beach. My topic was “HOW TO BUILD A MILLION USER ASP.NET MVC WEB APP”. I had a great time and I personally feel that it was my best talk yet. What really made it great was the awesome audience and questions asked.

A copy of the presentation which I did and the example code is available here.

Thanks again for the opportunity Code on the Beach! It has been a terrific conference.

Speaking at Code on the Beach This Weekend!

I’m speaking at Code on the Beach this weekend! My topic is “HOW TO BUILD A MILLION-USER ASP.NET MVC WEB APP” and I take the stage at 11:00am EST on Saturday in the Atlantica A conference room.

Please drop by and introduce yourself! I’ll be around all weekend and at the Beach Bar Saturday night. Hope to see you there!

Web API Mapping QueryString/Form Input

If you’re using the Web API as part of the MVC4 framework, you may encounter a scenario in which you must map parameters of strange names to variables for which characters of the name would be illegal. That wasn’t very clear, so let’s do this by example. Consider part of the Facebook API:

Firstly, Facebook servers will make a single HTTP GET to your callback URL when you try to add or modify a subscription. A query string will be appended to your callback URL with the following parameters:

hub.mode – The string “subscribe” is passed in this parameter
hub.challenge – A random string
hub.verify_token – The verify_token value you specified when you created the subscription

Now if we wanted to use Web API to receive this data, we know that C# does not support the decimal character ‘.’ existing in variable names. So, how do we bind this querystring data to variables of a different name?

After a lot of searching, I discovered that the answer is surprisingly simple – just use the FromUriAttribute:

public string Get([FromUri(Name="hub.mode")]string mode, 
    [FromUri(Name="hub.challenge")]string challenge, 
    [FromUri(Name="hub.verify_token")]string verifyToken)
{
    /* method body */
}

Works like a charm!

Presented at JaxDug

This past Wednesday (Jan 2 2013) I presented to the Jacksonville Developers User Group (JaxDug). My topic was MVC4. It was a great experience and I really appreciated and enjoyed the opportunity to talk to a great group of folks.

For those in the Jacksonville area, I highly recommend joining JaxDug. More information is available on their website (linked above) or the JaxDug LinkedIn group.

A copy of the presentation which I did and the example code is available here.

Thanks again for the opportunity JaxDug!

Custom Output Caching with MVC3 and .NET 4.0 – Done Properly!

I came across a need at work today to re-implement some of the Output Caching for our MVC3 application which runs under .NET 4.0. I wanted to use standard Output Caching (via the OutputCacheAttribute class, why re-invent the well-working wheel?) but due to some of our requirements I needed more control over how my objects were cached. More specifically, I needed to cache them with a custom Cache Dependency. With a little bit of Google-Fu, I was delighted to learn of the Output Cache Provider functionality introduced in ASP.NET 4. I implemented a custom OutputCacheProvider, registered it in my Web.config file as the Default Cache Provider, and I was well on my way.

…Or so I thought. You see, in our application we are caching both regular (Parent) and partial (Child) Controller Action Method calls. That is to say, we’re caching regular calls to Controller Action Methods, as well as the outputs of Child Action Method calls which are invoked from uncached Parent Action Method calls. While testing, some strange behaviour showed me that my Child Action Method calls were not being cached by my shiny new custom Output Cache Provider. They were instead being cached by the Default Output Cache Provider, which I have no control over. I confirmed this by debugging and seeing that my Child Action Method calls were not hitting my Custom Output Cache Provider methods… What gives?

I did some more Googling and learned very little, but happened to come across this little tidbit of somewhat vague information. I also came across a few .NET bloggers that had solved this problem… how shall I say… VERY poorly. So, I’d like to tell you how to do it correctly.

In the .NET 4.0 edition of the MVC3 assemblies, the OutputCacheAttribute contains a static property called ChildActionCache which is of type ObjectCache. As you can see from the MSDN page (at least at the time of writing this), they aren’t exactly detailing what it is for or how it really works – or why you can’t just use the bloody OutputCacheAttribute for Child Action Method calls. So what is going on?

Well, after a little investigation, I discovered the reasoning behind the madness. Basically, from a high level view, the OutputCacheAttribute works in conjunction with a Caching Http Module (the OutputCacheModule class). Each HTTP Request is passed to the OutputCacheModule LONG before it reaches your MVC application (FYI, this is called kernel-level IIS caching), and if the Http Module can pull a cached Response for that particular Request out of the Cache, it will short circuit your application and simply render the response to the user, stopping further execution. When this happens, your application never even sees the request. Neat, huh? If it can’t find the request, it exits and lets your application do its thing… And whenever you’ve placed OutputCache on your Action Method, it will cache the response in the same format that the Http Module looks for. This allows for MUCH less work to be done by your application in caching things. Cool, right?

You may now see why you cannot cache Child Action Method calls using the regular old OutputCacheAttribute… Your MVC application needs to execute a Parent Action Method from which the Child Action Methods are executed. If your Child Action Method was cached in the same way as a Parent Action Method, the HttpModule would always perform a Cache-miss since the Child Request originates from the Parent and has a completely different method signature, parameters, etc. upon which the Cache Key is derived. How can you cache your Child Action Methods ahead of your MVC application when your MVC application needs to execute in order to generate the Child Action Method Requests? And so, OutputCacheAttribute only works in the traditional manner for Parent Action Method calls.

So, how do you “fix” this and handle the Caching of Custom Child Action Methods in the same way as Parent Action Methods? First, create a custom class that inherits from the MemoryCache object. In this class you’re going to override 2 methods:

/// <summary>
/// A Custom MemoryCache Class.
/// </summary>
public class CustomMemoryCache : MemoryCache
{
    public CustomMemoryCache(string name)
        : base(name)
    {

    }
    public override bool Add(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
    {
        // Do your custom caching here, in my example I'll use standard Http Caching
        HttpContext.Current.Cache.Add(key, value, null, absoluteExpiration.DateTime, 
            System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);

        return true;
    }

    public override object Get(string key, string regionName = null)
    {
        // Do your custom caching here, in my example I'll use standard Http Caching
        return HttpContext.Current.Cache.Get(key);
    }
}

You’re going to build your custom Output Cache Provider (mentioned at the beginning of this post) similarly, inheriting from the abstract class OutputCacheProvider. Here’s an example one:

/// <summary>
/// A Custom OutputCacheProvider Class.
/// </summary>
public class CustomOutputCacheProvider : OutputCacheProvider
{
    public override object Add(string key, object entry, DateTime utcExpiry)
    {
        // Do the same custom caching as you did in your 
        // CustomMemoryCache object
        var result = HttpContext.Current.Cache.Get(key);

        if (result != null)
        {
            return result;
        }

        HttpContext.Current.Cache.Add(key, entry, null, utcExpiry,
            System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);

        return entry;
    }

    public override object Get(string key)
    {
        return HttpContext.Current.Cache.Get(key);
    }

    public override void Remove(string key)
    {
        HttpContext.Current.Cache.Remove(key);
    }

    public override void Set(string key, object entry, DateTime utcExpiry)
    {
        HttpContext.Current.Cache.Add(key, entry, null, utcExpiry,
            System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);
    }
}

You’ll notice that Add and Set are similar, but that Add checks for and returns the object from Cache if it exists, before attempting any Caching. This is the expected behaviour of the Add method according to MSDN and thus you should code it as above.

Now your Web.config needs a few simple lines added in order to be configured to use your CustomOutputCacheProvider:

<system.web>
  <caching>
    <outputCache defaultProvider="CustomProvider">
      <providers>
        <clear/>
        <add name="CustomProvider" type="MyMvcApp.Caching.CustomOutputCacheProvider, MyMvcApp"/>
      </providers>
    </outputCache>
  </caching>
</system.web>

The defaultProvider segment above allows you to set the Named Output Cache Provider that should be used by default for all Output Caching.

With the classes and configuration in place, you’ve now configured all Parent Action Methods which are decorated with [OutputCache] to use your new Custom Output Cache Provider! But we still need to configure Child Action Methods to do the same Caching as Parent Action Methods. This is where your custom MemoryCache object comes into play. Modify your Global.asax to wire your CustomMemoryCache into the OutputCacheAttribute:

protected void Application_Start()
{
    // Register Custom Memory Cache for Child Action Method Caching
    OutputCacheAttribute.ChildActionCache = new CustomMemoryCache("My Cache");
}

As an FYI, for your CustomMemoryCache object, and MemoryCache objects in general, here’s some information how to configure them by Name using your Web.config or App.config – very useful. You’ll note that I named my MemoryCache “My Cache” above – the name isn’t optional, but it has no effect unless it matches a Named Cache entry in your Web.config or App.config file; if it does match, it will adhere to the rules of your Named Cache. If, on the other hand, your MemoryCache object doesn’t use Runtime Caching and instead writes to a database or other external source such as AppFabric, the Named Cache will have no effect since it applies only to in-process Runtime Caching.

And that’s it! You’ve got a fully custom Output Caching solution in .NET 4.0 for your MVC3 application that correctly leverages standard Microsoft hooks and components! Thanks for reading this long post – comments and criticisms welcomed as always.