2010-07-30 48 views
3

比方說,我有Asp.net MVC2 IModelBinder正試圖推動我堅果(以及隨後的)

class FooClass { } 

class BarClass 
{ 
    public FooClass Foo; 
} 

這BarClass是我傳遞到的ViewPage模型。

我也通過(通過ViewData)一個IEnumerable<SelectListItem>與所有的Foo,並匹配bar.Foo被選中(在運行時檢查)。

然後我打電話Html.DropDownList("Foo", foos);

的下拉列表呈現很好,但它並沒有選擇合適的項目,因爲HTML控件有屬性的名稱,它與運行internaly的ViewData.Eval()食堂。這似乎是一個公認的行爲(見有關如此多的答案),所以我並不認爲有關和呼叫更改爲擴展名:

Html.DropDownList("DDL_Foo", foos); 

正確的價值選擇和我快樂。所以我把表格寄回。

不幸的是,在我的控制器的相應操作中,Foo成員爲空。所以我添加一個FooModelBinder,它實現了IModelBinder來攔截表單的DDL_Foo並正確地初始化FooClass。

但是FooModelBinder.BindModel從不被解僱,bar.Foo爲空。如果我再次更改我的視圖並將下拉列表重新命名爲Foo,則FooModelBinder會按預期觸發,並且bar.Foo會根據需要進行初始化。

那麼,我錯過了什麼?更重要的是,我該如何做到這一點。我想到了大量的黑客和解決方法,但這不是我想要的。我想知道如何做對。

謝謝!

[編輯] 感謝您的反饋,但我不認爲前綴是問題。

關於Binder,我添加它,因爲它不能被正確地初始化,否則。請注意,我正在處理的真實案例比這裏介紹的更復雜。這個解決方案只是我能做的最小的模型來重現問題。

這將在revelant代碼問(或download the full solution):

控制器

[HttpGet] 
    public ActionResult Index() 
    { 
     var dp = new DummyProvider(); 
     var bar = dp.GetBar(); 
     var foos = new List<SelectListItem>(); 

     dp.GetAllFoos().ForEach(
      f => foos.Add(new SelectListItem {Text = f.Name, Value = f.Id.ToString(), Selected = f.Id == bar.Foo.Id })); 

     ViewData["foos"] = foos; 

     return View(bar); 
    } 

    [HttpPost] 
    public ActionResult Index(BarClass bar) 
    { 
     var dp = new DummyProvider(); 
     var foos = new List<SelectListItem>(); 

     dp.GetAllFoos().ForEach(
      f => foos.Add(new SelectListItem { Text = f.Name, Value = f.Id.ToString(), Selected = f.Id == bar.Foo.Id })); 

     ViewData["foos"] = foos; 
     ViewData["selectedItem"] = bar.Foo.Name; 

     return View(bar); 
    } 

VIEW

<% 
    var foos = ViewData["foos"] as List<SelectListItem>; 

    using(Html.BeginForm()) 
    { 
     %> 
     <p> 
      <h3>Enter Another Value</h3> 
      <%= Html.TextBox("AnotherValue", Model.AnotherValue) %> 
     </p> 
     <p> 
      <h3>Enter Yet Another Value</h3> 
      <%= Html.TextBox("YetAnotherValue", Model.YetAnotherValue) %> 
     </p> 

     <p> 
      <h3>Choose a foo</h3> 
      <%= Html.DropDownList("DDL_Foo", foos)%> 
     </p> 
     <button type="submit">Send back !</button> 
     <% 
    } 
%> 

模型

public class BarClass 
{ 
    public FooClass Foo { get; set; } 
    public string AnotherValue { get; set; } 
    public string YetAnotherValue { get; set; } 
} 

public class FooClass 
{ 
    public Guid Id { get; set; } 
    public string Name { get; set; } 

} 

public class FooClassCollection : List<FooClass> { } 

public class FooModelBinder : IModelBinder 
{ 
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     var foo = new FooClass(); 

     var guid = Guid.Empty; 
     if (Guid.TryParse(controllerContext.HttpContext.Request.Form["DDL_Foo"], out guid)) 
     { 
      foo.Id = guid;  
     } 


     return foo; 
    } 
} 

public class DummyProvider 
{ 
    public FooClassCollection GetAllFoos() 
    { 
     return new FooClassCollection 
         { 
          new FooClass {Name = "Item 1", Id = new Guid("4a402abd-ab85-4065-94d6-d9fcc0f9b69e")}, 
          new FooClass {Name = "Item 2", Id = new Guid("cf20bfd6-0918-4ffc-a6ec-c4cc4ed30e7f")}, 
          new FooClass {Name = "Item 3", Id = new Guid("ad81b882-b93e-42b9-a42c-78376dd8f59d")}, 
          new FooClass {Name = "Item 4", Id = new Guid("1511c15d-9ae4-4b18-9e10-e02588c21b27")}, 
          new FooClass {Name = "Item 5", Id = new Guid("855e4a2f-fc5b-4117-a888-1dc3ebb990fc")}, 
         }; 
    } 

    public BarClass GetBar() 
    { 
     return new BarClass 
        { 
         AnotherValue = "Nice value", 
         YetAnotherValue = "This one is awesome", 
         Foo = new FooClass {Name = "Item 3", Id = new Guid("ad81b882-b93e-42b9-a42c-78376dd8f59d")} 
        }; 
    } 
} 

GLOBAL。ASAX

protected void Application_Start() 
    { 
     AreaRegistration.RegisterAllAreas(); 
     RegisterRoutes(RouteTable.Routes); 

     ModelBinders.Binders.Add(typeof(FooClass), new FooModelBinder()); 
    } 

[編輯]有一個懸而未決的問題上codeplex開放,如果你想解決,請選它,請(即使它已經將近一年,現在已經開放)。

+1

你應該發佈一些代碼。我的猜測是,你的命名在對象定義的某個地方是錯誤的,而你根本不需要自定義聯編程序。 – 2010-07-30 16:55:04

+0

看看我的相關問題,它可能會幫助你http://stackoverflow.com/questions/2563685/need-help-using-the-defaultmodelbinder-for-a-nested-model有一個類似的問題,它證明了命名約定。 – Will 2010-07-30 17:14:21

+0

感謝您的反饋。我按要求添加了代碼。 – 2010-07-30 20:14:09

回答

1

我設法讓一切由製作BarClassModelBinder做所有崗位上工作。代碼如下:

public class BarModelBinder : IModelBinder 
{ 
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     var bar = new BarClass(); 

     // In real code, check for nulls, etc. 

     bar.AnotherValue = controllerContext.HttpContext.Request.Form["AnotherValue"]; 
     bar.YetAnotherValue = controllerContext.HttpContext.Request.Form["YetAnotherValue"]; 

     var guid = Guid.Empty; 
     if (Guid.TryParse(controllerContext.HttpContext.Request.Form["DDL_Foo"], out guid)) 
     { 
      bar.Foo = new FooClass {Id = guid}; 
     } 


     return bar; 
    } 
} 

所以我在控制器中使用FormCollection時看到的唯一好處是代碼的清晰度。唯一讓我感到不舒服的是字段名稱在ModelBinder中是「隱藏的」,所以如果有人改變了視圖,他必須非常小心字段名稱。也許有一些方法可以繞過這個問題,也許有一個屬性。但即使沒有這一點,這是一個較小的邪惡,所以我會解決它。

整個問題仍然看起來像DropDownListFor實現的不良副作用。

0

剛花了半個小時玩這個。我不會爲編寫自定義模型活頁夾而煩惱。我只是使用視圖模型而不是整個FooClass,而是使用Guid FooId。反正你不會從下拉列表中獲得更多的信息。然後這將工作:

<%: Html.DropDownListFor(m => m.FooId, foos) %> 

當您回發它將正確綁定FooId屬性。

如果BarClass是域模型類,視圖模型看起來是這樣的(OBV):

public class BarViewModel 
{ 
    public Guid FooId { get; set; } 
    public string AnotherValue { get; set; } 
    public string YetAnotherValue { get; set; } 
} 
+0

謝謝你的建議,但這不是我正在尋找的解決方案。這實際上是我們到目前爲止使用的解決方案,但我想擺脫那些無用的特定模型。例如,如果我編寫我自己的DropDownList,它可以很好地工作。這個問題出現在System.Web.Mvc.Html.SelectExtensions的「SelectInternal」中,它在那裏得到htmlHelper.ViewData.Eval(name);出於我不明白的原因,然後重新初始化Selectitem列表中項目的Select值,這是我沒有得到的原因。我認爲這是爲它增加更多的「魔力」。我使用定製的DDL升級瞭解決方案。 – 2010-07-31 09:02:26

+0

我同意它的行爲非常怪異。我也被它弄糊塗了。 – Necros 2010-07-31 14:30:03