2010-07-30 8 views
3

이의 내가이 BarClass 내가 ViewPage에 전달하고있어 모델입니다Asp.net MVC2 IModelBinder는

class FooClass { } 

class BarClass 
{ 
    public FooClass Foo; 
} 

있다고 가정 해 봅시다 날 미치게하려고 (그리고 성공)한다.

나는 (ViewData를 통해) IEnumerable<SelectListItem>에 Foo를 모두 넣고 bar.Foo과 일치하는 항목을 선택합니다 (런타임에 선택). 그때 전화 했어

Html.DropDownList("Foo", foos);

드롭 다운리스트는 잘 렌더링하지만, HTML 컨트롤은 호텔의 이름이 있기 때문에 적절한 항목을 선택하지 않고는 internaly 실행 ViewData.Eval() 놨어요. 그것은 인정 된 행동을 것 같다 그래서 나는 그것에 대해 논쟁 아니에요, (SO에 그것에 대해 많은 답변을 보았다)과에 확장에 전화를 변경합니다

Html.DropDownList("DDL_Foo", foos); 

가 적절한 값을 선택하고 난 행복. 그래서 양식을 다시 게시합니다.

슬프게도, 내 컨트롤러의 적절한 액션에서 Foo 멤버는 null입니다. 따라서 IModelBinder을 구현하는 FooModelBinder을 추가하여 양식의 DDL_Foo를 가로 채고 FooClass를 올바르게 초기화하십시오.

그러나 FooModelBinder.BindModel은 절대 실행되지 않으며 bar.Foo은 null입니다. 내 뷰를 다시 변경하고 드롭 다운 목록의 이름을 Foo로 다시 변경하면 FooModelBinder가 예상대로 실행되고 bar.Foo가 초기화됩니다.

그래서 내가 무엇을 놓쳤는가? 그리고 더 중요한 것은 어떻게해야합니까? 길입니다. 나는 그것을 해킹하고 해결할 수있는 방법을 찾았지만, 그것이 내가 원하는 것이 아니다. 나는 그것을 올바르게하는 방법을 알고 싶다.

감사합니다.

[편집] 피드백을 보내 주셔서 감사합니다.하지만 접두사가 문제가되지는 않습니다.

바인더에 대해서는 제대로 초기화 할 수 없으므로 추가했습니다. 현재 진행중인 실제 사례는 여기서 제시되는 것보다 훨씬 복잡하다는 점에 유의하십시오. 이 솔루션은 문제를 재현하기 위해 할 수있는 가장 작은 모형입니다. 여기

그것은하여 revelant 코드는 요청 (또는 download the full solution) :

CONTROLLER

[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

일부 코드를 게시해야합니다. 내 생각 엔 당신이 개체 정의 어딘가에 이름이 틀렸고, 사용자 정의 바인더가 전혀 필요 없다는 것입니다. –

+0

내 관련 질문을 확인해보십시오. http://stackoverflow.com/questions/2563685/need-help-using-the-defaultmodelbinder-for-a-nested-model에서 비슷한 문제가 있었고 명명 규칙이있다. – Will

+0

의견에 감사드립니다. 요청 된대로 코드를 추가했습니다. –

답변

1

모든 작업을 수행하는 BarClassModelBinder함으로써 작업 모든 것을 얻을 수 있습니다. 다음은 코드입니다.

컨트롤러에서 FormCollection을 사용하여 다시 볼 수있는 유일한 이점은 명확한 코드입니다. 내가 편안하지 않은 유일한 점은 필드 이름이 ModelBinder에서 "숨겨져있다"는 것입니다. 따라서 누군가가 뷰를 변경하면 필드 이름을 신중하게 선택해야합니다. 어쩌면 그 문제를 도출 할 수있는 방법이 있을지도 모르겠다. 하지만 그게 없으면, 이것은 더 나쁜 것이 아닙니다. 그래서 나는 그것에 정착 할 것입니다.

전체적인 문제는 여전히 DropDownListFor 구현의 바람직하지 않은 부작용처럼 보입니다.

0

반 시간 만에 놀아 보았습니다. 나는 커스텀 모델 바인더를 쓰는 것에 신경 쓰지 않을 것이다. 나는 단지 FooClass이 아닌 뷰 모델을 사용 하겠지만 대신 Guid FooId을 사용한다. 당신은 어쨌든 드롭 다운리스트에서 더 많은 것을 얻지 못할 것입니다. 그런 다음이 작동합니다 :

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

다시 게시 할 때 제대로 FooId 속성을 바인딩합니다. BarClass 도메인 모델 클래스 인 경우

, 뷰 모델이 (OBV)처럼 보일 수있다 : I 관리

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 명부에있는 품목의 선정 가치를 다시, 나 얻지 않는 이유를 위해, 재 초기화하십시오. 나는 그것에 "마법"을 더하는 것이라고 생각한다. 나는 작동하는 커스텀 DDL로 솔루션을 업그레이드했다. –

+0

나는 그것이 아주 이상하게 행동한다는 것에 동의한다. 나는 또한 그것으로 혼란스러워했다. – Necros