2010-03-08 2 views
3

저는 오히려 MVC에 익숙하지 않습니다. 그리고 점점 더 전체 프레임 워크에 들어감에 따라 모델 바인더가 유지하기가 어려워지고 있습니다. MVC 뷰, 모델 및 모델 바인더를 가능한 한 깨끗하게 유지하려면 어떻게해야합니까?

설명해 드리죠

...

나는 기본 CRUD - 이상 - 데이터베이스 응용 프로그램을 쓰고 있어요. 내 도메인 모델은 매우 풍부해질 것입니다. 가능한 한 씬 컨트롤러를 유지하기 위해 Create/Edit 명령에서 작업 매개 변수가 도메인 모델의 풍부한 인스턴스로 설정되도록 설정했습니다. 이렇게하려면 사용자 정의 모델 바인더를 구현했습니다.

그러나 결과적으로이 사용자 지정 모델 바인더는보기 및 모델에 매우 한정적입니다. MVC 2와 함께 제공되는 DefaultModelBinder를 재정의하기로 결정했습니다. 내 모델에 바인딩되는 필드가 텍스트 상자 (또는 단순한 항목) 일 경우 기본 메서드로 위임합니다. 그러나 드롭 다운이나 좀 더 복잡한 작업을 할 때 (UI는 날짜와 시간이 별도의 데이터 입력 필드이지만 모델의 경우 하나의 속성 임), 일부 검사와 일부 수동 데이터 조작을 수행해야합니다.

이 결과는 뷰와 바인더 사이에 매우 긴밀한 관계가 있다는 것입니다. 나는 이것으로 구조적으로 훌륭하지만 코드 유지 관점에서 볼 때 악몽이다.

예를 들어, 여기에 바인딩 할 내 모델은 Log 유형입니다 (이것은 내 액션에서 매개 변수로 가져올 객체입니다). "ServiceStateTime"은 Log의 등록 정보입니다. "log.ServiceStartDate"및 "log.ServiceStartTime"의 형식 값 완전히 임의적이며

protected override object GetPropertyValue(ControllerContext controllerContext, 
               ModelBindingContext bindingContext, 
               PropertyDescriptor propertyDescriptor, 
               IModelBinder propertyBinder) 
{ 
     if (propertyDescriptor.Name == "ServiceStartTime") 
     { 
      string date = bindingContext.ValueProvider.GetValue("log.ServiceStartDate").ConvertTo(typeof (string)) as string; 
      string time = 
       bindingContext.ValueProvider.GetValue("log.ServiceStartTime").ConvertTo(typeof (string)) as string; 
      DateTime dateTime = DateTime.Parse(date + " " + time); 
      return dateTime; 
     } 
     if (propertyDescriptor.Name == "ServiceEndTime") 
     { 
      string date = bindingContext.ValueProvider.GetValue("log.ServiceEndDate").ConvertTo(typeof(string)) as string; 
      string time = 
       bindingContext.ValueProvider.GetValue("log.ServiceEndTime").ConvertTo(typeof(string)) as string; 
      DateTime dateTime = DateTime.Parse(date + " " + time); 
      return dateTime; 
     } 

제() ... Html.TextBox ("log.ServiceStartTime") 형태의 두 텍스트 박스 온 Log.ServiceEndTime도 비슷한 필드입니다.

이것은 나에게 매우 마른 느낌이 들지 않습니다. 첫째, ServiceStartTime 또는 ServiceEndTime을 다른 필드 이름으로 리팩터링하면 텍스트 문자열이 누락 될 수 있습니다. (리팩터링 도구 인 R #이 이런 종류의 일에 상당히 능숙하지만 빌드 타임 오류는 발생하지 않습니다. 수동 테스트에서만 잡힐 수 있습니다.) 둘째, "log.ServiceStartDate"및 "log.ServiceStartTime"설명자를 임의로 변경하기로 결정한 경우 동일한 문제가 발생합니다. 저에게 런타임 무성 오류는 최악의 오류입니다. 뷰와 모델 사이에 공통으로

  • 리팩터링 텍스트 문자열 :

    그래서, 나는 몇 가지 옵션이 여기에 도움이 이러한 문제의 일부를 가로 질러 온 사람들의 의견을 받고 싶어요 참조 컨트롤러에서 aspx/ascx 뷰로 전달하는 ViewModel 객체에 연결된 const 문자열로 바인더 아웃. 하지만 ViewModel 객체를 오염시킵니다.

  • 모든 상호 작용에 대해 단위 테스트를 제공하십시오. 나는 단위 테스트의 큰 지지자이며이 옵션을 풀어 내지 못했지만 발을 깎지 않고 나를 구할 수 없다는 느낌을 받았습니다.

시스템 로그 및 기타 엔티티는 Fluent NHibernate를 사용하여 데이터베이스에 유지됩니다. 나는 정말 내 컨트롤러를 가능한 한 얇게 유지하고 싶다.

그래서 여기에있는 모든 의견을 크게 환영합니다!

감사

답변

1

당신은 뷰 모델을 사용할 수는 :

class ViewModelClass 
{ 
    [DateValidationAttribute] 
    public property DateTime ServiceStartTimeDate { get; set; } 
    [TimeValidationAttribute] 
    public property DateTime ServiceStartTimeTime { get; set; } 
} 

DefaultModelBinder 바인더는 이러한 필드를 결합하는 어떤 수정이 필요하지 않습니다. 그런 다음

class DateUtil 
{ 
    public static DateTime CombineDateAndTime(DateTime Date, DateTime Time); 
} 

당신이 데이터베이스에서 개체를 얻을 : 그럼 당신은이 필드 결합하는 기능을 쓸 수

var entity = context.GetUpdatedEntity(id); 
entity.ServiceStartTime = DateUtil.CombineDateAndTime(viewModel.ServiceStartTimeDate,viewModel.ServiceStartTimeTime); 

당신이 정말로 모델 바인더를 갖고 싶어하는 경우를, 당신은 이런 식으로 작업을 수행 할 수 있습니다

public class DateAndTimeModelBinder : DefaultModelBinder 
{ 
    protected override object GetPropertyValue(ControllerContext controllerContext, 
              ModelBindingContext bindingContext, 
              PropertyDescriptor propertyDescriptor, 
              IModelBinder propertyBinder) 
    { 
     if (propertyDescriptor.PropertyType == typeof(DateTime)) 
     { 
      string time = bindingContext.ValueProvider.GetValue(CreateSubPropertyName(bindingContext.ModelName, propertyDescriptor.Name + "Time")).ConvertTo(typeof(string)) as string; 
      var date = base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder); 
      if ((date != null) && (time != null)) 
       return CombineDateAndTime(date, time); 
     } 
     return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder); 
    } 
} 

날짜 시간 필드가 결합 될 때, 바인더는 우리가 "ServiceStartTime", 그것은 "ServiceStartTimeTime"을 찾아 바인딩 경우 추가 양식 필드는 날짜 포를 결합하므로, (마지막에 "시간"에있는 경우 본다 "ServiceStartTime"필드 및 시간 부분을 "ServiceStartTimeTime"으로 변경). 있을 경우 날짜에 값을 추가합니다. 한 번 쓰면 모든 분야에서 효과가 있습니다. 위의 코드는 제대로 작동하지 않을 것이며, 약간의 조정이 필요하지만 아이디어를 보여줍니다.

+0

저는이 ViewModel 클래스를 점점 더 많이 고려해 왔습니다. ViewModel과 Model이 거의 1 : 1 인 경우 아키텍쳐가 복잡해질 것이라는 점이 걱정입니다. –

+0

@MBonig : 따라서 모델 클래스에만 setter가있는 속성을 추가 할 수 있습니다. "ServiceStartTimeTime"설정자는 시간 부분 만 설정하고 "ServiceStartTimeDate"는 날짜 부분을 설정합니다. 더 나은 솔루션 인 ModelBinder를 사용할 수도 있습니다. – LukLed