2013-08-01 85 views
3

我有一个表格,动态生成很多复选框(700+)。 IE中的表单加载非常缓慢(但几乎没有加载铬),当我发布表单时,它几乎锁定了我的Web浏览器。MVC性能问题与许多领域

我该如何调试这个问题,或者我是否做了导致此性能问题的错误?或者这是一个巨大的形式,我应该尝试分裂它。

这里是我的控制器:

public ActionResult Create(int id) 
    { 
      var model = new TaskRequestViewModel 
       { 
        Task = new Task(), 
        Components = db.Component.ToList() 
         …. 
       } 
      return View(model);   
    } 

要简单介绍一下我的模型,我有大约10个组件,每个组件都有约10子组件,每个子组件具有约10个选项(复选框)提供。导致所述700多个字段(然后是几个隐藏字段)。当有更少的复选框(100ish)时,它工作正常。

我的看法是这样的(3个嵌套循环):

  @for (int cI = 0; cI < Model.Components.Count; cI++) 
      { 
        @Html.HiddenFor(x => Model.Components[cI].ComponentId) 

        @for (int scI = 0; scI < Model.Components[cI].SubComponents.Count; scI++) 
        { 
          @Html.DisplayFor(x => Model.Components[cI].SubComponents[scI].Name)         
          @Html.HiddenFor(x => Model.Components[cI].SubComponents[scI].SubComponentId) 

          @for (int t = 0; t < Model.Components[cI].SubComponents[scI].TaskTypes.Count; t++) 
          { 
           @Html.HiddenFor(x => Model.Components[cI].SubComponents[scI].TaskTypes[t].SubComponentTaskTypeId) 
           @Html.HiddenFor(x => Model.Components[cI].SubComponents[scI].TaskTypes[t].TaskTypeId) 
           @Html.CheckBoxFor(x => Model.Components[cI].SubComponents[scI].TaskTypes[t].Active) 
          } 

        } 

      } 
+0

您是否正在使用某种客户端技术(如javascript/jQuery)来修饰或以其他方式控制输入? – OzrenTkalcecKrznaric

+0

还有没有办法减少这个数量或将其分成多个页面?或者像@OzrenTkalčecKrznarić所说的那样,尽可能地减少客户端的负担。 – Leniency

+0

@效率:这不是我的第一个想法,但你是对的。我认为OP可能会使用复选框的jQuery装饰,即使对于具有50-100个输入的页面,这也可能是致命的。 – OzrenTkalcecKrznaric

回答

5

如果您使用Visual Studio性能分析器,您会看到第二级索引访问器的渲染时间开始拍摄。 HiddenFor,EditorFor,LabelFor等扩展可以相当激烈,因为它们会反映您的模型,以在生成HTML时确定数据注释的ID,名称和存在。

随着渲染时间的延长,浏览器趋于冻结,因为它开始获取一些HTML,但没有得到它,所以它暂停。即使加载了所有的HTML,任何尝试加载运行的JavaScript库都需要评估(可能在很多领域 - 这取决于您的业务逻辑,如果您使用的是Modernizr,Html5Shi [v | m],等等)当你提交如果你使用不显眼的验证,你可能会得到冻结,因为每个字段被枚举和检查验证以及任何OnSubmit类型的逻辑。

为了加快您的渲染:

当你的模型使用儿童多层次,该执行得到了许多记录确实昂贵。不幸的是,将每个级别移动到它自己的部分视图并传递父级集合并不能解决问题100%。虽然它确实能够加快速度,但您的型号名称已不再适当地生成。

例如,您的某个孩子的输入标签上的Name属性看起来像“Components [17] .SubComponents [33] .TaskTypes [5] .SubComponentTaskTypeId”基于您的Model具有完整的链上下文。如果您将模型分解为部分视图并将模型指定为TaskType,则当您尝试在第18个Component上的第34个SubComponent上渲染第6个模型时,该名称现在将为“TaskType.SubComponentTaskTypeId”(假定您的模型是在一个循环)或“TaskType [5] .SubComponentTaskTypeId”(假设您的模型是一次IEnumerable调用)。

这意味着模型联编程序将不再知道如何将提交的表单转换回完整的对象。

对于我们我们用编辑模板解决了这个问题。这些基本上都是部分视图,但父项的上下文存储为前缀属性,该属性自动预先添加到字段名称。它的表现稍微好一点(如果你在做这么多的领域,仍然不是很好),并且名称仍然会以模型联编程序在发布到您的操作时会理解的方式生成。

如果您使用的编辑器模板的属性,你只需要使用

@for (int t = 0; t < Model.Components[cI].SubComponents[scI].TaskTypes.Count; t++) 
{ 
    @Html.EditorFor(m => m.TaskType[t]) 
} 

如果本身就是从子[N],它本身是从组件[M]被称为一个编辑模板称为编辑模板那么你会保持全名解析。这是因为每个级别都会抓取前缀一次并存储,同时呈现儿童,从而消除了重复反映模型以生成名称的需要。

作为一个奖励你的看法,因为更可读一点,你的显示逻辑现在被关注(数据类型)而不是混合在一起。

要诊断上提交冻结:

你需要运行一些JavaScript性能分析,看看是怎么运行以及何时。看看正在加载哪些库,看看你是否需要它们。如果你已经在你的包和_layout中获得了Modernizr的默认MVC引用,那么你就会从中受到巨大的冲击。这部分调试很难引导,因为它依赖于正在使用的客户端库。

编辑:在与有这种情况的同事交谈时(每行有3个级别的儿童,每行有3个输入,3个链接和5个标签),他仍然遇到了渲染这么多问题针对报告行数异常高的特定用户的记录。有太多的数据浏览器变得不稳定,偶尔会崩溃。

他最终使用JavaScript呈现模板来帮助解决问题。从本质上讲,他加载了每一行的父母以及一个展开链接。点击它时,他会使用AJAX为孩子获取JSON对象,并使用渲染模板生成HTML。当然,你必须小心地正确设置id和name属性,以便模型联编程序在提交时将其选中,但对他来说更好,因为没有1个父级有太多的行,导致问题按需呈现。我们的业务逻辑允许这样做,因为复选框基本上是作为从单个父母到子女的连锁,但不在父母之间运作。意思是当用户尝试工作或提交变更时,并非所有父母都完全加载并不重要。这是一个解决方法,因为客户端绝对不会接受分页,但也不能确保他们保持合理数量的开放记录!

5

另一件需要注意的事情是使用客户端验证时的Html.HiddenFor。

当您使用jquery.validate.unobtrusive.js使用船只在MVC项目中,您将使用脚本中的默认渲染束这样的:

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

(注:很可能已经改了名字,这是默认的)

当你有这样的,任何Html.HiddenFor将呈现这样的:

<input data-val="true" data-val-required="The XYZ field is required." name="XYZ" type="hidden" value=""> 

不用说,这些字段不需要启用客户端验证。用户无法看到它们,也无法与它们交互,并且应始终由模型填充。

来解决这个问题的方法是很容易的:

@Html.HiddenFor(x => x.XYZ, new { data_val = "false" }) 

如果不需要客户端验证,只要确保不包括包。

关于此更多解读here