1

我有一個包含選項列表的問題列表。他們的問題有一個類型字段,允許問題是一個單選按鈕或清單等...ASP.NET MVC模型綁定問題與嵌套集合

我想以這樣的方式創建視圖,我會回到問題和選項。現在我不關心答覆。

盡我所能告訴我遵循ASP.NET開箱即用的模型綁定規則,這些規則綁定了以下兩個地方的博客。

斯科特Hanselman的博客:http://bit.ly/1fYAWCs
菲爾哈克的博客:http://bit.ly/1fYBokd

是否有可能做什麼,我試圖與開箱模型綁定做,或者我需要實現我自己的自定義模型粘合劑?

如果我確實需要實現一個自定義模型聯編程序,您能指出我一些直截了當的例子或博客文章如何去做?

最終,我想使用問題列表和答案列表在數據註釋中編寫自定義屬性以驗證模型(通過確保每個問題至少有一個答案),同時還使用了不顯眼的驗證客戶。

如果有更好的解決方案,我願意接受。

下面是一個人爲的例子來證明我的問題。

視圖模型:

public class Questionnaire 
{ 
    private List<QuestionResponse> _questions; 

    [UIHint("QuestionResponse")] 
    public List<QuestionResponse> Questions 
    { 
     get { return _questions ?? (_questions = new List<QuestionResponse>()); } 
     set { _questions = value; } 
    } 

    public List<int> Responses; 
} 

型號:

public class QuestionResponse 
{ 
    public int QuestionId; 

    public string QuestionType; 

    public string QuestionCode; 

    public string QuestionName; 

    public string OpenResponse; 

    public List<QuestionOptions> Options; 
} 

public class QuestionOptions 
{ 
    public int OptionId; 

    public string OptionType; 

    public string OptionName; 

    public string OptionDescription; 

    public string OptionResponse; 

    public bool Selected; 

} 

控制器方法:

[HttpGet] 
    public ActionResult Questionnaire() 
    { 
     #region HardCodedDataz 
     var questions = new List<QuestionResponse> 
     { 
      new QuestionResponse 
      { 
       QuestionId = 1, 
       OpenResponse = null, 
       QuestionCode = "1", 
       QuestionName = "What kind of movies do you like?", 
       QuestionType = "Checklist", 
       Options = new List<QuestionOptions> 
       { 
        new QuestionOptions 
        { 
         OptionDescription = "Horror", 
         OptionId = 1, 
         OptionName = "Horror", 
         OptionResponse = null, 
         OptionType = "boolean", 
         Selected = false 
        }, 
        new QuestionOptions() 
        { 
         OptionDescription = "Family", 
         OptionId = 2, 
         OptionName = "Family", 
         OptionResponse = null, 
         OptionType = "boolean", 
         Selected = false 
        }, 
        new QuestionOptions() 
        { 
         OptionDescription = "Comedy", 
         OptionId = 3, 
         OptionName = "Comedy", 
         OptionResponse = null, 
         OptionType = "boolean", 
         Selected = false 
        }, 
        new QuestionOptions() 
        { 
         OptionDescription = "Adventure", 
         OptionId = 4, 
         OptionName = "Adventure", 
         OptionResponse = null, 
         OptionType = "boolean", 
         Selected = false 
        }, 
        new QuestionOptions() 
        { 
         OptionDescription = "Drama", 
         OptionId = 5, 
         OptionName = "Drama", 
         OptionResponse = null, 
         OptionType = "boolean", 
         Selected = false 
        } 
       } 
      }, 
      new QuestionResponse() 
      { 
       QuestionId = 2, 
       OpenResponse = null, 
       QuestionCode = "2", 
       QuestionName = "Which university did you attend in your first year of college?", 
       QuestionType = "RadioButton", 
       Options = new List<QuestionOptions> 
       { 
        new QuestionOptions() 
        { 
         OptionDescription = "Cornell", 
         OptionId = 1, 
         OptionName = "Cornell", 
         OptionResponse = null, 
         OptionType = "boolean", 
         Selected = false 
        }, 
        new QuestionOptions() 
        { 
         OptionDescription = "Columbia", 
         OptionId = 2, 
         OptionName = "Columbia", 
         OptionResponse = null, 
         OptionType = "boolean", 
         Selected = false 
        }, 
        new QuestionOptions() 
        { 
         OptionDescription = "Harvard", 
         OptionId = 3, 
         OptionName = "Harvard", 
         OptionResponse = null, 
         OptionType = "boolean", 
         Selected = false 
        }, 
        new QuestionOptions() 
        { 
         OptionDescription = "Yale", 
         OptionId = 4, 
         OptionName = "Yale", 
         OptionResponse = null, 
         OptionType = "boolean", 
         Selected = false 
        }, 
        new QuestionOptions() 
        { 
         OptionDescription = "Princeton", 
         OptionId = 5, 
         OptionName = "Princeton", 
         OptionResponse = null, 
         OptionType = "boolean", 
         Selected = false 
        } 
       } 
      } 
     }; 
     #endregion 

     var model = new Questionnaire() 
     { 
      Questions = questions, 
      Responses = new List<int>() 
     }; 

     return PartialView(model); 
    } 

    [HttpPost] 
    public JsonResult Questionnaire(List<QuestionResponse> questions, List<int> responses) 
    { 
     return Json(new 
     { 
      success = "some success message", 
      error = "some error message" 
     }); 
    } 

瀏覽:

@using TestApplication.Models 
@model Questionnaire 

@using (Html.BeginForm("Questionnaire", "Home", FormMethod.Post)) 
{ 
    <div> 
     @Html.EditorFor(m => m.Questions) 
    </div> 
    <div> 
     <button type="submit">Save</button> 
    </div> 
} 

自定義編輯模板:
注:隱藏字段僅用於填充在POST請求的模型的緣故。

@using System.Web.Mvc.Html 
@using TestApplication.Models 
@model List<QuestionResponse> 
@if (Model != null) 
{ 
    for (int i = 0; i < Model.Count; i++) 
    { 
     <p><strong>@Model[i].QuestionName</strong></p> 
     @Html.HiddenFor(m => m[i].QuestionName); 
     @Html.HiddenFor(m => m[i].QuestionType) 
     @Html.HiddenFor(m => m[i].QuestionCode) 
     @Html.HiddenFor(m => m[i].QuestionId) 

     if (Model[i].QuestionType == "Checklist") 
     { 
      for (int j = 0; j < Model[i].Options.Count; j++) 
      { 
       <div> 
        <label>@Model[i].Options[j].OptionDescription</label> 
        @Html.CheckBoxFor(m => m[i].Options[j].Selected) 
        @Html.HiddenFor(m => m[i].Options[j].OptionDescription) 
        @Html.HiddenFor(m => m[i].Options[j].OptionName) 
        @Html.HiddenFor(m => m[i].Options[j].OptionId) 
        @Html.HiddenFor(m => m[i].Options[j].OptionType) 
       </div> 
      } 
     } 
     else if (Model[i].QuestionType == "RadioButton") 
     { 
      for (int j = 0; j < Model[i].Options.Count; j++) 
      { 
       var name = "Questions[" + i + "].Options[" + j + "].Selected"; 
       var id = Model[i].Options[j].OptionId; 
       <div> 
        <label>@Model[i].Options[j].OptionDescription</label> 
        <input id="@id" type="radio" name="@name" value="" /> 
        @Html.HiddenFor(m => m[i].Options[j].OptionDescription) 
        @Html.HiddenFor(m => m[i].Options[j].OptionName) 
        @Html.HiddenFor(m => m[i].Options[j].OptionId) 
        @Html.HiddenFor(m => m[i].Options[j].OptionType) 
       </div> 
      } 
     } 
    } 
} 
+0

那麼,有什麼實際問題? –

+0

我無法讓模型在發佈請求上填充到服務器端。 – will

回答

1

尋找到asp.net源代碼後,我發現,這個框架期待你的模型組成的屬性和簡單的領域是行不通的。

以下是源代碼中的BindProperties方法。如果您的複雜模型沒有屬性,則屬性集合將爲空,並且綁定發生的for循環將會退出。

private void BindProperties(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     PropertyDescriptorCollection properties = GetModelProperties(controllerContext, bindingContext); 
     Predicate<string> propertyFilter = bindingContext.PropertyFilter; 

     // Loop is a performance sensitive codepath so avoid using enumerators. 
     for (int i = 0; i < properties.Count; i++) 
     { 
      PropertyDescriptor property = properties[i]; 
      if (ShouldUpdateProperty(property, propertyFilter)) 
      { 
       BindProperty(controllerContext, bindingContext, property); 
      } 
     } 
    } 

如果您絕對必須使用簡單的字段,那麼自定義模型聯編程序將是適當的。

同樣,如果你試圖在公共領域使用DataAnnotations那麼你會遇到這個問題 DataAnnotations on public fields vs properties in MVC