为什么ASP.NET MVC默认的模型绑定器很慢?做好工作需要很长时间 [英] Why ASP.NET MVC default Model Binder is slow? It's taking a long time to do its work
问题描述
在当前的项目中,客户要求以两种方式回答调查问卷的可能性:使用向导
(一次一个问题)和以单一形式列出
(所有问题一次)。这两种方式都已经实现了。
这个问题是从数据库的手册一章中使用AJAX加载的(这是超快的)。目前最大的一章有 230
问题(每个都有4个HTML输入字段 - 输入/文本,选择等)。如果用户选择这样的章节在列表
格式中回答,< form>
将包含约code> 920 要发布到服务器的字段。
我正在做一个AJAX POST请求,通过jQuery的 serialize
方法:
data:$(#questions:input)。 serialize()
此序列化需要 207.143ms
去完成。我在Firefox中使用Firebug调试此值:
console.profile();
$(#questions:input)。serialize();
console.profileEnd();
再次这是超快...
当使用以下操作方法保持接收到的数据时,问题出在:
public async任务< ActionResult> ListSaveAsync(IEnumerable< AnswerViewModel>问题)
如您所见,发布的数据是绑定到 IEnumerable< AnswerViewModel>问题
。 AnswerViewModel
只有4个字段来存储每个答案。
事情是需要相当长的时间(精确的10秒钟),点击保存按钮打到这个动作方法上的一个断点,也就是说,这个10秒钟被用在模型绑定器中。
要提到的是,我使用史蒂夫·桑德森的。
创建 AnswerViewModel
客户端的JavaScript对象,并将它们传递给action方法。这会减轻模型绑定吗?
滚动我自己的模型绑定器...但我真的不知道它是否会比默认的更快自带ASP.NET MVC。从我读过的默认模型绑定器做了大量反思,设置值/水合物的动作模型参数,这可能是瓶颈。
使用 FormCollection
,并枚举通过键获取每个值的已发布数据,并手动执行验证,如这里。
你还建议什么?
更新1
我选择了3,并实现了一个自定义的模型绑定器: AnswerModelBinder:IModelBinder
并将其用于该特定的操作方法: / p>
public async任务< ActionResult> ListSaveAsync(
[ModelBinder(typeof(AnswerModelBinder))]列表< AnswerViewModel>问题)
现在采取 10秒
完成只需 2秒
。
- 看起来默认模型绑定器验证检查[
ModelState
]对性能有很大的影响。
更新2
我刚刚经历过一次:有一个 List< Guid>
作为一个动作参数,只通过 $。getJson $ c $ c> call需要〜3秒钟来触发动作方法第一行的断点。将参数类型更改为
列表< string>
使得整个事情都在眨眼之间。
有趣的事实是在action方法之内我做了这个:
列表&Guid。 userIds = resources.Select(Guid.Parse).ToList();
,它会转换资源列表< string>
到列表< Guid>
即时。
肯定有一些bug与ASP.NET模型绑定。我只想知道是什么...:)
您可以使用ServiceStack JsonSerializer,结果
这里是文档
http://mono.servicestack .net / docs / text-serializers / json-serializer
这里是基准
http://mono.servicestack.net/benchmarks/
In a current project the client asked for the possibility of answering a questionnaire in two ways: using a Wizard
(one question at a time) and Listing
(all questions at once) in a single form. Both ways are already implemented.
The questions are loaded from the database per Manual's chapter using AJAX (this is super fast). The biggest chapter at the moment has 230
questions (each with 4 HTML input fields - input/text, select, etc). If the user selects such Chapter to answer in the Listing
format, the <form>
will contain at about 920
fields to be posted to the server.
I'm doing an AJAX POST request passing the data with jQuery's serialize
method:
data: $("#questions :input").serialize()
This serialization takes 207.143ms
to complete. I got this value debugging with Firebug in Firefox:
console.profile();
$("#questions :input").serialize();
console.profileEnd();
Again this is super fast...
The problem comes when hydrating the data received on the following action method:
public async Task<ActionResult> ListSaveAsync(IEnumerable<AnswerViewModel> questions)
As you see, the posted data is data bound to an IEnumerable<AnswerViewModel> questions
. AnswerViewModel
has only 4 fields to store each answer.
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.
An important thing to mention is that I'm using Steve Sanderson's @Html.BeginCollectionItem helper to help when materializing the ViewModel collection properties from the HTTP POST. See how the data gets in the ViewModel (Keys):
Do you know what I can try to do to optimize this?
I thought about 4 workarounds:
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
<form>
as this guy suggests here.Create
AnswerViewModel
JavaScript objects on the client side and pass them to the action method. Would this alleviate the Model Binder?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.
Use
FormCollection
and enumerate through the posted data getting each value by key and performing validation manually as shown here.
What else do you suggest?
Update 1
I went with option 3 and implemented a custom Model Binder: AnswerModelBinder : IModelBinder
and used it in that specific action method:
public async Task<ActionResult> ListSaveAsync(
[ModelBinder(typeof(AnswerModelBinder))]List<AnswerViewModel> questions)
Now what took 10 seconds
to complete takes only 2 seconds
.
- Looks like the default model binder validation checks [
ModelState
] has a big impact on performance.
Update 2
I just experienced it once again: having a List<Guid>
as an action parameter and passing only 59 strings
through a $.getJson
call was taking ~3 seconds to hit a breakpoint in the 1st line of the action method. Changing the parameter type to List<string>
made the whole thingy work in the blink of an eye.
An interesting fact is that inside the action method I did this:
List<Guid> userIds = resources.Select(Guid.Parse).ToList();
and it transforms the resources List<string>
to a List<Guid>
instantaneously.
For sure there's something buggy with ASP.NET model binder. I just would like to know what it is... :)
You can use ServiceStack JsonSerializer which is pretty fast in benchmark results here is documentation http://mono.servicestack.net/docs/text-serializers/json-serializer and here is the benchmarks http://mono.servicestack.net/benchmarks/
这篇关于为什么ASP.NET MVC默认的模型绑定器很慢?做好工作需要很长时间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!