ASP.NET MVC HiddenFor Causing Performance Issues When Client Side Validation Enabled

I ran into a really interesting performance problem in ASP.NET MVC 4.
An MVC view was taking almost 30 seconds of time on document.ready.
After digging into the issue I discovered that jquery.validate.unobtrusive.js that ships with MVC is surprisingly slow. However, the page I was looking at had very few fields on it. Or so it seemed…

Lets look at an example…

hiddenfor1

Here is a web page with a few input boxes on it. Surprisingly, the Chrome dev tools show that the page takes almost 1.2 seconds to load and there is a big gap of nothing after the files are loaded.

This simple page makes use of the client side form validation provided by jquery.validate.unobtrusive.js that ships with MVC. It is enabled by including this pre-configured bundle that is setup for you in the MVC project template:

    @Scripts.Render("~/bundles/jqueryval")

If we remove this bundle and reload our test page it no longer has client side form validation but it also loads much faster. Chrome dev tools now reports the page loading in .6 seconds.

hiddenfor2

So what is it about this page that causes jQuery form validation to take an extra .6 seconds?

Here is the model we are using for the page:

using System.ComponentModel.DataAnnotations;

namespace MvcApplication2.Models
{
    public class LargeModel
    {
        [Required] public string Prop1 { get; set; }
        [Required] public string Prop2 { get; set; }
        ...
        [Required] public string Prop20 { get; set; }
    }
}

The controller:

using System.Web.Mvc;
using MvcApplication2.Models;

namespace MvcApplication2.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            var records = new[]
                          {
                              new LargeModel() {},
                              new LargeModel() {},
                              new LargeModel() {},
                              new LargeModel() {},
                              new LargeModel() {},
                              new LargeModel() {},
                              new LargeModel() {},
                              new LargeModel() {},
                              new LargeModel() {},
                              new LargeModel() {},
                              new LargeModel() {}
                          };
            return View(records);
        }
    }
}

And finally the page itself:

@model MvcApplication2.Models.LargeModel[]

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
    <head>
        <title>title</title>
        <script src="~/Scripts/jquery-2.0.2.min.js"></script>
        @Scripts.Render("~/bundles/jqueryval")
    </head>
    <body>
        @using (Html.BeginForm())
        {
            for (var i = 0; i < Model.Length; i++)
            {<div>
                @Html.TextBoxFor(x => Model[i].Prop1)
                @Html.HiddenFor(x => Model[i].Prop2)
                ...
                @Html.HiddenFor(x => Model[i].Prop20)
            </div>}
        }
    </body>
</html>

So to sum this all up we simply have a model with 20 properties. The controller returns a list of 10 of them to be displayed. The razor view then loops over them all, displays a normal text box for one of the properties and does a @Html.HiddenFor() for the others.

It might seem odd to drop a bunch of hidden fields in the page, but think about all the “intro to MVC” articles you have ever read. Generally everyone illustrates MVC by putting the entire model into the page in form fields using hidden ones for fields that aren’t used so that the entire model gets posted back to the server for subsequent requests. However having the abundance of hidden fields causes a performance and scaling problem especially when client side form validation is enabled. Why? Well, because this is what @Html.HiddenFor() generates:

<input data-val="true" data-val-required="The Prop2 field is required." name="[0].Prop2" type="hidden" value="">

Note the extra attributes for validation. The HiddenFor() helper actually generates fields that have client side form validation enabled… for elements that the user can’t see or edit!

When jquery.validate.unobtrusive.js is included it runs some JavaScript on document.ready() to find all the input elements in the page that that have validation enabled and setup the validation. This step is very slow. On my i7 processor laptop in the latest Chrome version it can only process 64 input boxes per second. That might not seem like a lot, but remember at the beginning I said I was looking at a page that was taking almost 30 seconds to load? The developers had put every property from nested models in hidden elements. A generated page contained about 1,700 hidden input boxes! 1700 / 64 = 26.5 seconds of JavaScript processing time just to validate input boxes that the user can’t see!

So how do we fix this? Fortunately it is simple. Just turn off client side validation for hidden input elements. We can do this by setting the data-val attribute to false. In razor syntax, it looks like this:

@Html.HiddenFor(x => Model[i].Prop2, new { data_val = "false" })

Note that the dash ‘-‘ for the attribute name becomes an underscore ‘_’ in C#.

With data validation turned off, out page now goes back to loading in .6 seconds. So with this simple change, we cut our page load time in half. So, why does the @Html.HiddenFor() helper even bother putting data validation on these fields anyway? My best guess is that it was unintentional. Both Html.TextBoxFor() and Html.HiddenFor() both call the same code internally to generate their HTML element but set the type to either “text” or “hidden”.

Advertisements
Tagged with: ,
Posted in Programming
2 comments on “ASP.NET MVC HiddenFor Causing Performance Issues When Client Side Validation Enabled
  1. escobar4400 says:

    Thank you. That’s help me

  2. dcmglqmj says:

    it’s a security issue probably. imagine changing the values of hidden fields manually and then making a fake request back to sever. the contents of those fields that were not validated can contain harmful code/values.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

CodingWithSpike is Jeff Valore. A professional software engineer, focused on JavaScript, Web Development, C# and the Microsoft stack. Jeff is currently a Software Engineer at Virtual Hold Technologies.


I am also a Pluralsight author. Check out my courses!

%d bloggers like this: