2010-03-27 4 views
0

이전에 답변을 받았지만 받아 볼만한 솔루션을 찾기 위해 지난 3 시간을 보냈습니다. 아무 것도 찾을 수 없으므로 죄송합니다. 물론 반복합니다.부모님/자식 관계가있는 모델 바인딩

두 개의 도메인 개체 인 Player와 Position이 있습니다. 플레이어는 포지션을 가지고 있습니다. 내 도메인 개체는 NHibernate로 내 데이터베이스에 연결된 POCO입니다. Player를 사용하는 Add 액션이있어서 내장 된 모델 바인딩을 사용하고 있습니다. 내보기에는 사용자가 플레이어의 위치를 ​​선택할 수있는 드롭 다운 목록이 있습니다. 드롭 다운 목록의 값은 위치의 ID입니다. 모델 바인딩 시점에서 ID 만 있고 다른 필수 속성은 없으므로 Position 객체가 유효성 검사에 실패한 것을 제외하고는 (ModelState.IsValid) 모든 것이 올바르게 채워집니다.

ASP.NET MVC 2에서이 문제를 해결하기위한 가장 좋은 해결책은 무엇입니까? 내가 해봤

솔루션 ...

  1. 이 ModelState.IsValid 전에 ID를 기반으로 데이터베이스에서 위치를 가져 오기는 내 컨트롤러의 추가 행동이라고합니다. 유효성 검사를 다시 실행하도록 모델을 가져올 수 없으므로 ModelState.IsValid는 항상 false를 반환합니다.
  2. 기본 바인더에서 상속 된 사용자 지정 ModelBinder를 만들고 기본 바인더가 호출 된 후 데이터베이스에서 위치를 가져옵니다. ModelBinder는 유효성 검사를하고있는 것처럼 보이므로 기본 바인더에서 아무 것도 사용하면 묶여 있습니다. 즉, 제 자신의 바인더를 완전히 굴려 폼에서 모든 값을 가져와야한다는 것을 의미합니다 ... 이것은 일반적인 사용 사례의 경우 실제로 잘못되어 비효율적 인 것처럼 보입니다. 나는 작동 할 수 있습니다 생각

솔루션, 난 그냥 Player에서 사용하는 경우

    는 위치 클래스에 대한 검증을 끄고
  1. ...하는 방법을 알아낼 수 없습니다.
  2. 사용자 정의 ModelBinder 작성은 대부분의 특성 바인딩에 기본 바인더를 사용하지만 기본 바인더가 유효성 검사를 실행하기 전에 데이터베이스에서 위치를 가져올 수 있습니다.

그럼 나머지는 어떻게 해결할 수 있습니까?

감사합니다,

P.S. 내 생각에 플레이어에 대한 PositionId를 가지고있는 것은 좋은 해결책이 아닙니다. 보다 세련된 방식으로 해결할 수 있어야합니다.

답변

3

이 특정 문제뿐만 아니라 일반적으로보기에 도메인 모델이있는 대신 별도의 ViewModel을 만듭니다. 따라서 귀하의 경우 도메인 모델을 과도하게 노출하지 않아도 (그리고 필요없는 사항을 얻지 않아도) 유효성 검사를하지 않는 것이 가장 최악의 해결책 일 수 있습니다.

+0

기본적으로 나는 모든 견해에 대해 DTO를 만들어야합니다. 나에게 꽤 비효율적 인 것 같아. 다른 누구에게도 해결책이 있습니까? – DFX

+0

나는 처음에 그것에 대해 들었을 때 같은 생각을했습니다. 그러나 테스트와 문제 분리의 긍정적 측면이 항상 승리하는 것처럼 보입니다. 나에게 종종 특정 모델을 디버깅하거나 사용자 정의 모델 바인더를 만드는 것보다 다른 모델을 만드는 것이 더 빠릅니다. –

0

이 케이스를 검증 할 수있는 사용자 정의 ModelBinder.

+0

Xeb, 맞춤형 ModelBinders에 대한 좋은 정보를 찾는 데 어려움이 있습니다. 바로 저를 직접 지적 해 줄 수 있다고 생각하십니까? – DFX

0

내 응용 프로그램 중 하나에서 동일한 문제가 발생했습니다. 나는 DefaultModelBinder로부터 상속받은 커스텀 IModelBinder를 생성하여 그것을 해결하고 바인딩 할 특정 타입으로 그것을 등록했다.

트릭은 을 사용하여 데이터베이스에 쿼리하지 않고 엔티티에 대한 형식화 된 참조 만 작성하는 것입니다.당신의 예에서 (Read more about session.Load here in Ayende's blog)

/// <summary> 
    /// Base for binding references. Usually displayed in dropdowns 
    /// </summary> 
    public class ReferenceBinder<T> : DefaultModelBinder 
     where T : class 
    { 

     private readonly ISession session; 

     public ReferenceBinder(ISession session) 
     { 
      this.session = session; 
     } 


     public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
     { 

      var idName = CreateSubPropertyName(bindingContext.ModelName, "ID"); 

      ValueProviderResult result = bindingContext.ValueProvider.GetValue(idName); 

      int value; 
      return (int.TryParse(result.AttemptedValue, out value)) ? this.session.Load<T>(value) : null; 

     } 


    } 

당신의 Global.asax에서 이런 짓을 할 것이다 :

ModelBinders.Binders.Add(typeof(Position), new ReferenceBinder<Position>(<pass your current session implementation or use DI/IoC>)); 

(I 실제 바인더를 만들어 내 구현 세션을 채울 성 윈저를 사용하고 있습니다) 도메인 모델을 가정

입니다 :

 public class Player { 
      public virtual Position Position {get;set;} 
     } 

     public class Position { 
      public virtual int ID {get;private set;} 
     } 

그리고 당신의 다시 게시는 다음과 같습니다.

"Position.ID" = <id from dropdown> 

편집 : 전용 뷰 모델에서는 DTO 방식을 사용해야합니다. 나는 이것을 나중에 배웠다. Have a look here for a quick start.