为什么 ASP.NET MVC 默认模型绑定器很慢?完成它的工作需要很长时间 [英] Why ASP.NET MVC default Model Binder is slow? It's taking a long time to do its work

查看:14
本文介绍了为什么 ASP.NET MVC 默认模型绑定器很慢?完成它的工作需要很长时间的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在当前项目中,客户要求以两种方式回答问卷的可能性:使用Wizard(一次一个问题)和Listing(所有问题)一次)以单一形式.两种方式都已经实现.

问题是使用 AJAX 从数据库中按照手册章节加载的(这非常快).目前最大的一章有 230 个问题(每个有 4 个 HTML 输入字段 - 输入/文本、选择等).如果用户选择这样的章节以 Listing 格式回答,

将包含大约 920 字段以发布到服务器.

我正在执行 AJAX POST 请求,使用 jQuery 的 serialize 方法传递数据:

data: $("#questions :input").serialize()

这个序列化需要 207.143ms 来完成.我在 Firefox 中使用 Firebug 调试了这个值:

console.profile();$("#questions :input").serialize();控制台.profileEnd();

再次,这是超级快...

在对以下操作方法接收到的数据进行水合时出现问题:

public async TaskListSaveAsync(IEnumerable 问题)

如您所见,发布的数据是绑定到 IEnumerable 的数据.问题.AnswerViewModel 只有 4 个字段来存储每个答案.

问题是,在单击保存"按钮到此操作方法上的断点后需要相当长的时间(准确地说是 10 秒),也就是说,这 10 秒大概是花费在模型绑定器中的.

>

需要提及的重要一点是,我正在使用 Steve Sanderson 的 .

  • 在客户端创建 AnswerViewModel JavaScript 对象并将它们传递给操作方法.这会减轻 Model Binder 的影响吗?

  • 滚动我自己的模型活页夹...但我真的不知道它是否会比 ASP.NET MVC 附带的默认活页夹更快.从我读到的默认模型绑定器做了很多反射来设置值/水合物动作的模型参数,这可能是瓶颈.

  • 使用 FormCollection 并枚举发布的数据,通过键获取每个值并手动执行验证,如图 这里.

  • 你还有什么建议?

    <小时>

    更新 1

    我选择了选项 3 并实现了一个自定义模型绑定器:AnswerModelBinder : IModelBinder 并在该特定操作方法中使用它:

    public async Task列表保存异步([ModelBinder(typeof(AnswerModelBinder))]List问题)

    现在用10秒完成的事情只需要2秒.

    • 看起来默认模型绑定器验证检查 [ ModelState ] 对性能有很大影响.

    更新 2

    我刚刚又经历了一次:将 List 作为动作参数,并且仅通过 $.getJson 传递 59 个字符串> 调用需要大约 3 秒才能在操作方法的第一行中达到断点.将参数类型更改为 List 使整个事情在眨眼间工作.

    一个有趣的事实是,在 action 方法中我是这样做的:

    ListuserIds = resources.Select(Guid.Parse).ToList();

    并且它立即将资源List转换为List.

    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:

    1. 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.

    2. Create AnswerViewModel JavaScript objects on the client side and pass them to the action method. Would this alleviate the Model Binder?

    3. 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.

    4. 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屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆