2013-08-01 83 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