2012-11-21 91 views
3

我有一個包含多態對象列表的ViewModel。在MVC3中綁定嵌套集合

public class InputParameters 
{ 
    public InputParameters() 
    { 
     InputPrompts = new List<IInputPrompt>(); 
    } 

    public string Name { get; set; } 
    public string Path { get; set; } 

    public IList<IInputPrompt> InputPrompts { get; set; } 
} 

這反過來看這個:

public interface IInputPrompt 
{ 
    string Name { get; set; } 
    bool IsHidden { get; set; } 
    bool IsRequired { get; set; } 

    dynamic Value { get; set; } 
} 
public class TextPrompt : IInputPrompt 
{ 
    public string Name { get; set; } 
    public bool IsHidden { get; set; } 
    public bool IsRequired { get; set; } 
    public dynamic Value { get; set; } 
} 
public class MultiSelectPrompt : IInputPrompt 
{ 
    public string Name { get; set; } 
    public bool IsHidden { get; set; } 
    public bool IsRequired { get; set; } 
    public dynamic Value { get; set; } 

    public MultiSelectList Values 
    { 
     get 
     { 
      return new MultiSelectList(((IDictionary<int, string>)Value).ToList(), "Key", "Value"); 
     } 
    } 
} 

有一個編輯器視圖模板爲每個派生類型,觀點看這本:

@model OptionListModelBinding.Models.InputParameters 
@{ 
    ViewBag.Title = "Index"; 
} 
<h2>Index</h2> 
@using (Html.BeginForm()) 
{ 
    <fieldset> 
     <legend><strong>@Model.Name</strong></legend> 
     <div><em>@Model.Path</em></div> 
     <div>@Html.EditorFor(p => p.InputPrompts)</div> 
     <div><input type="submit" /></div> 
    </fieldset> 

} 
// editor template 
@model OptionListModelBinding.Models.InputParameters 
@{ 
    ViewBag.Title = "Index"; 
} 
<h2>Index</h2> 
@using (Html.BeginForm()) 
{ 
    <fieldset> 
     <legend><strong>@Model.Name</strong></legend> 
     <div><em>@Model.Path</em></div> 
     <div>@Html.EditorFor(p => p.InputPrompts)</div> 
     <div><input type="submit" /></div> 
    </fieldset> 

} 
// editor template 
@model OptionListModelBinding.Models.MultiSelectPrompt 
@{ 
    ViewBag.Title = "MultiSelectPrompt"; 
} 

<h2>MultiSelectPrompt</h2> 
<div><strong>@Model.Name</strong></div> 
<div><em>@Html.ListBox(Model.Name, Model.Values)</em></div> 

這一切都呈現很好:Screen Capture

問題是這樣的:

我該如何將它綁定回模型?我有一個自定義模型聯編程序:(原諒這個代碼的黑客性質)。

public class InputParameterModelBinder : IModelBinder 
{ 
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     if (bindingContext == null) 
     { 
      throw new ArgumentNullException("bindingContext"); 
     } 
     // iterate over the form fields 
     foreach (string item in controllerContext.HttpContext.Request.Form.AllKeys) 
     { 
      // this is where the problem is ... I cannot use the generic here. 
      var dog = FromPostedData<string>(bindingContext, item); 

     } 

     InputParameters userInput = new InputParameters(); 

     return userInput; 
    } 

    // Dino Esposito code 
    private T FromPostedData<T>(ModelBindingContext context, string id) 
    { 
     if (String.IsNullOrEmpty(id)) return default(T); 
     var key = String.Format("{0}.{1}", context.ModelName, id); 
     var result = context.ValueProvider.GetValue(key); 
     if (result == null && context.FallbackToEmptyPrefix) 
     { 
      result = context.ValueProvider.GetValue(id); 
      if (result == null) return default(T); 
     } 

     context.ModelState.SetModelValue(id, result); 

     T valueToReturn = default(T); 
     try 
     { 
      valueToReturn = (T)result.ConvertTo(typeof(T)); 
     } 
     catch { } 
     return valueToReturn; 
    } 
} 

編輯 我提到的是,在列表中的項目在運行時確定?

編輯 這是報告生成工具的前端。後端服務提供可用報告的列表以及運行每個報告所需的參數。在編譯時這些都不是已知的,並且報告定義甚至可能隨着需要重新編譯的門戶網站而改變。

我可以有一個可變數量和類型的輸入參數。

+0

你所有的類對於你的參數模型來說都是什麼樣的? –

+0

@ErikPhilips第二個代碼塊顯示提示/參數視圖模型類。雖然在我回顧整個問題時,我似乎從我的代碼中演化出來了。 – THBBFT

回答

0

我不認爲你需要自定義綁定。您可以將您的視圖模型簡單地傳遞迴POST方法:

[HttpPost] 
public ActionResult ProcessForm(InputParameters viewModel) 
{ 
    ... 
} 

默認MVC粘結劑將匹配與InputParameters參數的屬性表單值。您可能需要在該參數上使用[Bind]屬性來指示變量名稱前綴,如果您的視圖使用任何特殊形式變量名稱,則binder必須查找該變量名稱前綴。

+0

我先試過了。它在處理用戶輸入的Action之前引發「無法實例化接口」異常。我認爲這是因爲它無法確定要實例化哪個子對象(TextPrompt或MultiSelectPrompt)。 – THBBFT

0

你不能指望MVC實例化一個IInputPrompt,因爲它沒有邏輯來找出你真正想要的類。你的選擇是創建一個模型綁定到模型綁定到這個特定的接口,或者將接口變成一個具體的類。

+0

我最終在ModelBinder中使用反射來確定集合中每個類的類型。 – THBBFT