Note that there are some explanatory texts on larger screens.

plurals
  1. POWhy ASP.NET MVC default Model Binder is slow? It's taking a long time to do its work
    primarykey
    data
    text
    <p>In a current project the client asked for the possibility of answering a questionnaire in two ways: using a <code>Wizard</code> (one question at a time) and <code>Listing</code> (all questions at once) in a single form. Both ways are already implemented.</p> <p>The questions are loaded from the database per Manual's chapter using AJAX (this is super fast). The biggest chapter at the moment has <code>230</code> questions (each with 4 HTML input fields - input/text, select, etc). If the user selects such Chapter to answer in the <code>Listing</code> format, the <code>&lt;form&gt;</code> will contain at about <code>920</code> fields to be posted to the server.</p> <p>I'm doing an AJAX POST request passing the data with jQuery's <code>serialize</code> method:</p> <pre><code>data: $("#questions :input").serialize() </code></pre> <p>This serialization takes <code>207.143ms</code> to complete. I got this value debugging with Firebug in Firefox:</p> <pre><code>console.profile(); $("#questions :input").serialize(); console.profileEnd(); </code></pre> <p>Again this is super fast...</p> <p>The problem comes when hydrating the data received on the following action method:</p> <pre><code>public async Task&lt;ActionResult&gt; ListSaveAsync(IEnumerable&lt;AnswerViewModel&gt; questions) </code></pre> <p>As you see, the posted data is data bound to an <code>IEnumerable&lt;AnswerViewModel&gt; questions</code>. <code>AnswerViewModel</code> has only 4 fields to store each answer.</p> <p>The thing is that it takes a considerable amount of time (precisely 10 seconds) after clicking the Save button to hit a breakpoint on this action method, that is, those 10 seconds are being spent in the model binder presumably.</p> <p>An important thing to mention is that I'm using Steve Sanderson's <a href="http://blog.stevensanderson.com/2008/12/22/editing-a-variable-length-list-of-items-in-aspnet-mvc/" rel="nofollow noreferrer">@Html.BeginCollectionItem helper</a> to help when materializing the ViewModel collection properties from the HTTP POST. See how the data gets in the ViewModel (Keys):</p> <p><img src="https://i.stack.imgur.com/QjA5e.png" alt="enter image description here"></p> <p>Do you know what I can try to do to optimize this?</p> <p>I thought about 4 workarounds:</p> <ol> <li><p>Save back only the modified questions. To do this I'd need to store each answer value in a data-attribute when loading the listing and compare it with the actual value when submitting the <code>&lt;form&gt;</code> as this guy suggests <a href="https://stackoverflow.com/a/8043489/114029">here</a>.</p></li> <li><p>Create <code>AnswerViewModel</code> JavaScript objects on the client side and pass them to the action method. Would this alleviate the Model Binder?</p></li> <li><p>Roll my own model binder... but I really don't know if it would be faster than the default one that comes with ASP.NET MVC. From what I've read the default model binder does a lot of reflection to set the values/hydrate the action's model parameter and this could be the bottleneck.</p></li> <li><p>Use <code>FormCollection</code> and enumerate through the posted data getting each value by key and performing validation manually as shown <a href="http://stack247.wordpress.com/2011/03/20/iterate-through-system-web-mvc-formcollection/" rel="nofollow noreferrer">here</a>.</p></li> </ol> <p>What else do you suggest?</p> <hr> <p><strong>Update 1</strong></p> <p>I went with option 3 and implemented a custom Model Binder: <code>AnswerModelBinder : IModelBinder</code> and used it in that specific action method:</p> <pre><code>public async Task&lt;ActionResult&gt; ListSaveAsync( [ModelBinder(typeof(AnswerModelBinder))]List&lt;AnswerViewModel&gt; questions) </code></pre> <p>Now what took <code>10 seconds</code> to complete takes only <code>2 seconds</code>.</p> <ul> <li>Looks like the default model binder validation checks [ <code>ModelState</code> ] has a big impact on performance.</li> </ul> <p><strong>Update 2</strong></p> <p>I just experienced it once again: having a <code>List&lt;Guid&gt;</code> as an action parameter and passing only <code>59 strings</code> through a <code>$.getJson</code> call was taking ~3 seconds to hit a breakpoint in the 1st line of the action method. Changing the parameter type to <code>List&lt;string&gt;</code> made the whole thingy work in the blink of an eye.</p> <p>An interesting fact is that inside the action method I did this:</p> <pre><code>List&lt;Guid&gt; userIds = resources.Select(Guid.Parse).ToList(); </code></pre> <p>and it transforms the resources <code>List&lt;string&gt;</code> to a <code>List&lt;Guid&gt;</code> instantaneously.</p> <p>For sure there's something buggy with ASP.NET model binder. I just would like to know what it is... :)</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload