在当前的一个项目中,客户要求以两种方式回答问卷的可能性:以单一形式使用Wizard
(一次一个问题)和(一次所有问题)。Listing
这两种方式都已经实现。
这些问题是使用 AJAX 根据手册的章节从数据库中加载的(这非常快)。目前最大的章节有230
问题(每个都有 4 个 HTML 输入字段 - 输入/文本、选择等)。如果用户选择这样的章节以Listing
格式回答,<form>
则将包含920
要发布到服务器的关于字段。
我正在做一个 AJAX POST 请求,使用 jQuery 的serialize
方法传递数据:
data: $("#questions :input").serialize()
这个序列化需要207.143ms
完成。我在 Firefox 中使用 Firebug 调试了这个值:
console.profile();
$("#questions :input").serialize();
console.profileEnd();
再次,这是超级快...
当对以下操作方法接收到的数据进行水合时,问题就出现了:
public async Task<ActionResult> ListSaveAsync(IEnumerable<AnswerViewModel> questions)
如您所见,发布的数据是绑定到IEnumerable<AnswerViewModel> questions
. AnswerViewModel
只有 4 个字段来存储每个答案。
问题是,在单击 Save 按钮后,需要相当长的时间(精确 10 秒)才能在此操作方法上命中断点,也就是说,这 10 秒大概是在模型绑定器中花费的。
需要提及的重要一点是,当从 HTTP POST 实现 ViewModel 集合属性时,我正在使用 Steve Sanderson 的@Html.BeginCollectionItem 助手来提供帮助。查看数据如何进入 ViewModel(键):
你知道我可以尝试做些什么来优化它吗?
我想到了4个解决方法:
只保存修改后的问题。为此,我需要在加载列表时将每个答案值存储在数据属性中,并在提交时将其与实际值进行比较,
<form>
正如这个人在这里建议的那样。在客户端创建
AnswerViewModel
JavaScript 对象并将它们传递给 action 方法。这会减轻 Model Binder 的负担吗?滚动我自己的模型绑定器......但我真的不知道它是否会比 ASP.NET MVC 附带的默认模型更快。从我读到的默认模型绑定器做了很多反思来设置值/水合动作的模型参数,这可能是瓶颈。
使用并枚举发布的数据,通过键获取每个值并手动执行验证,如此处
FormCollection
所示。
你还有什么建议?
更新 1
我选择了选项 3 并实现了一个自定义模型绑定器:AnswerModelBinder : IModelBinder
并在该特定操作方法中使用它:
public async Task<ActionResult> ListSaveAsync(
[ModelBinder(typeof(AnswerModelBinder))]List<AnswerViewModel> questions)
现在10 seconds
完成的只需要2 seconds
.
- 看起来默认模型绑定器验证检查 [
ModelState
] 对性能有很大影响。
更新 2
我刚刚再次体验到它:将 aList<Guid>
作为操作参数并仅59 strings
通过$.getJson
调用需要大约 3 秒才能在操作方法的第一行遇到断点。更改参数类型以List<string>
使整个事情在眨眼之间工作。
一个有趣的事实是,在 action 方法中我这样做了:
List<Guid> userIds = resources.Select(Guid.Parse).ToList();
并将资源List<string>
转换为List<Guid>
瞬间。
ASP.NET 模型绑定器肯定有一些问题。我只是想知道它是什么...... :)