POST 작업에서 뷰 모델을 도메인 모델에 다시 매핑하는 방법은 무엇입니까?


87

ViewModels 및 Automapper 사용에 대한 인터넷에서 찾은 모든 기사는 "Controller-> View"방향 매핑의 지침을 제공합니다. 모든 선택 목록과 함께 도메인 모델을 하나의 특수한 ViewModel로 가져 와서보기에 전달합니다. 명확하고 괜찮습니다.
보기에는 양식이 있으며 결국 POST 작업에 있습니다. 여기서 모든 Model Binder 는 바인딩 및 유효성 검사를위한 명명 규칙의 일부에서 원래 ViewModel 과 [분명히] 관련된 다른 View Model 과 함께 [분명히] 장면에 표시됩니다 .

도메인 모델에 어떻게 매핑합니까?

삽입 작업으로합시다. 동일한 Automapper를 사용할 수 있습니다. 하지만 업데이트 작업이라면 어떨까요? 리포지토리에서 도메인 엔터티를 검색하고 ViewModel의 값에 따라 속성을 업데이트하고 리포지토리에 저장해야합니다.

부록 1 (2010 년 2 월 9 일) : 때때로 모델의 속성을 할당하는 것만으로는 충분하지 않습니다. View Model의 값에 따라 Domain Model에 대한 조치를 취해야합니다. 즉, 도메인 모델에서 일부 메서드를 호출해야합니다. 아마도 뷰 모델을 처리하기 위해 컨트롤러와 도메인 사이에있는 일종의 애플리케이션 서비스 계층이 있어야합니다.


이 코드를 구성하는 방법과 다음 목표를 달성하기 위해 어디에 배치해야합니까?

  • 컨트롤러를 얇게 유지
  • SoC 관행을 존중
  • 도메인 기반 설계 원칙을 따릅니다.
  • 건조하다
  • 계속하려면 ...

답변:


37

나는 사용 IBuilder의 인터페이스를하고 사용하여 구현 ValueInjecter을

public interface IBuilder<TEntity, TViewModel>
{
      TEntity BuildEntity(TViewModel viewModel);
      TViewModel BuildViewModel(TEntity entity);
      TViewModel RebuildViewModel(TViewModel viewModel); 
}

... (구현) RebuildViewModelBuildViewModel(BuilEntity(viewModel))

[HttpPost]
public ActionResult Update(ViewModel model)
{
   if(!ModelState.IsValid)
    {
       return View(builder.RebuildViewModel(model);
    }

   service.SaveOrUpdate(builder.BuildEntity(model));
   return RedirectToAction("Index");
}

btw 나는 ViewModel을 쓰지 않습니다. 입력은 훨씬 짧기 때문에 작성하지만
도움이되기를 바랍니다.

업데이트 : 현재 ProDinner ASP.net MVC 데모 앱 에서이 접근 방식을 사용하고 있습니다. 이제 IMapper 라고합니다.이 접근 방식을 자세히 설명하는 pdf도 제공됩니다.


나는이 접근 방식을 좋아합니다. 내가 명확하지 않은 한 가지는 IBuilder의 구현, 특히 계층화 된 응용 프로그램에 비추어 볼 때입니다. 예를 들어, 내 ViewModel에는 3 개의 SelectList가 있습니다. 빌더 구현은 저장소에서 선택 목록 값을 어떻게 검색합니까?
Matt Murrell

@Matt Murrell은 prodinner.codeplex.com봅니다. 저는 거기에서이 작업을 수행하고 IBuilder 대신 IMapper라고 부릅니다
Omu

6
저는이 접근 방식이 마음에 들어 여기에 샘플을 구현했습니다. gist.github.com/2379583
Paul Stovell

내 생각에는 도메인 모델 접근 방식을 준수하지 않습니다. 불분명 한 요구 사항에 대한 CRUD 접근 방식처럼 보입니다. 합리적인 조치를 전달하기 위해 도메인 모델에서 공장 (DDD) 및 관련 방법을 사용하면 안됩니까? 이런 식으로 DB에서 엔티티를로드하고 필요에 따라 업데이트하는 것이 좋습니다. 따라서 완전히 정확하지 않은 것 같습니다.
Artyom

7

AutoMapper와 같은 도구를 사용하여 원본 개체의 데이터로 기존 개체를 업데이트 할 수 있습니다. 업데이트를위한 컨트롤러 작업은 다음과 같습니다.

[HttpPost]
public ActionResult Update(MyViewModel viewModel)
{
    MyDataModel dataModel = this.DataRepository.GetMyData(viewModel.Id);
    Mapper<MyViewModel, MyDataModel>(viewModel, dataModel);
    this.Repostitory.SaveMyData(dataModel);
    return View(viewModel);
}

위의 스 니펫에서 볼 수있는 것과는 별개로 :

  • 모델을보기위한 POST 데이터 + 유효성 검사는 ModelBinder에서 수행됩니다 (사용자 지정 바인딩으로 확장 가능).
  • 오류 처리 (예 : 저장소에서 발생하는 데이터 액세스 예외 포착)는 [HandleError] 필터로 수행 할 수 있습니다.

컨트롤러 작업은 매우 얇고 우려 사항이 분리되어 있습니다. 매핑 문제는 AutoMapper 구성에서 해결되고 유효성 검사는 ModelBinder에서 수행되며 데이터 액세스는 Repository에서 수행됩니다.


6
Automapper가 평탄화를 역전시킬 수 없기 때문에 여기서 유용할지 모르겠습니다. 결국 도메인 모델은보기 모델과 같은 단순한 DTO가 아니므로 일부 속성을 할당하는 것만으로는 충분하지 않을 수 있습니다. 아마도 View Model의 내용에 따라 Domain Model에 대해 일부 작업을 수행해야 할 것입니다. 그러나 +1은 꽤 좋은 접근 방식을 공유합니다.
Anthony Serdyukov

@Anton ValueInjecter는 평탄화를 반전시킬 수 있습니다;)
Omu

이 접근 방식을 사용하면 컨트롤러를 얇게 유지하지 않고 SoC 및 DRY를 위반합니다.
Rookian

5

클라이언트 상호 작용의 양방향에 대해 ViewModel이라는 용어를 재사용한다고 말하고 싶습니다. 야생에서 충분한 ASP.NET MVC 코드를 읽었다면 ViewModel과 EditModel의 차이를 보았을 것입니다. 중요하다고 생각합니다.

ViewModel은 뷰를 렌더링하는 데 필요한 모든 정보를 나타냅니다. 여기에는 정적 비대화 형 장소에서 렌더링되는 데이터와 정확히 렌더링 할 항목을 결정하기위한 검사를 수행하기위한 데이터가 포함될 수 있습니다. 컨트롤러 GET 작업은 일반적으로 해당 뷰에 대한 ViewModel 패키징을 담당합니다.

EditModel (또는 ActionModel)은 사용자가 해당 POST에 대해 수행하려는 작업을 수행하는 데 필요한 데이터를 나타냅니다. 따라서 EditModel은 실제로 액션을 설명하려고합니다. 이것은 아마도 ViewModel에서 일부 데이터를 제외시킬 것이며 관련이 있지만 실제로 다르다는 것을 깨닫는 것이 중요하다고 생각합니다.

하나의 아이디어

즉, Model-> ViewModel에서 이동하는 AutoMapper 구성과 EditModel-> Model에서 이동할 다른 구성을 매우 쉽게 가질 수 있습니다. 그런 다음 다른 컨트롤러 작업은 AutoMapper를 사용하기 만하면됩니다. EditModel은 모델에 대한 속성의 유효성을 검사하고 해당 값을 모델 자체에 적용하는 기능을 가질 수 있습니다. 다른 작업을 수행하지 않고 MVC에 ModelBinders가있어 어쨌든 요청을 EditModel에 매핑합니다.

또 다른 아이디어

그 외에도 최근에 제가 생각했던 ActionModel의 아이디어에서 이런 종류의 작업은 클라이언트가 다시 게시하는 것은 실제로 사용자가 수행 한 여러 작업에 대한 설명이며 하나의 큰 데이터 덩어리가 아니라는 것입니다. 이것은 확실히 관리하기 위해 클라이언트 측에 일부 Javascript가 필요하지만 아이디어가 흥미 롭다고 생각합니다.

기본적으로 사용자가 제시 한 화면에서 작업을 수행 할 때 Javascript는 작업 개체 목록을 만들기 시작합니다. 예를 들어 사용자가 직원 정보 화면에있을 수 있습니다. 직원이 최근에 결혼했기 때문에 성을 업데이트하고 새 주소를 추가합니다. 내부적으로 이것은 목록에 a ChangeEmployeeNameAddEmployeeMailingAddress객체를 생성 합니다. 사용자가 '저장'을 클릭하여 변경 사항을 커밋하고 각 작업을 수행하는 데 필요한 정보 만 포함 된 두 개체 목록을 제출합니다.

더 지능적인 ModelBinder가 필요하지만, 좋은 JSON 시리얼 라이저는 클라이언트 측 작업 개체와 서버 측 작업 개체의 매핑을 처리 할 수 ​​있어야합니다. 서버 측 (2 계층 환경에있는 경우)은 작업하는 모델에서 작업을 완료 한 메서드를 쉽게 가질 수 있습니다. 따라서 컨트롤러 작업은 모델 인스턴스가 가져올 Id와 이에 대해 수행 할 작업 목록을 얻습니다. 또는 행동에 이드가있어 매우 분리되어 있습니다.

따라서 아마도 다음과 같은 것이 서버 측에서 실현 될 수 있습니다.

public interface IUserAction<TModel>
{
     long ModelId { get; set; }
     IEnumerable<string> Validate(TModel model);
     void Complete(TModel model);
}

[Transaction] //just assuming some sort of 2-tier with transactions handled by filter
public ActionResult Save(IEnumerable<IUserAction<Employee>> actions)
{
     var errors = new List<string>();
     foreach( var action in actions ) 
     {
         // relying on ORM's identity map to prevent multiple database hits
         var employee = _employeeRepository.Get(action.ModelId);
         errors.AddRange(action.Validate(employee));
     }

     // handle error cases possibly rendering view with them

     foreach( var action in editModel.UserActions )
     {
         var employee = _employeeRepository.Get(action.ModelId);
         action.Complete(employee);
         // against relying on ORMs ability to properly generate SQL and batch changes
         _employeeRepository.Update(employee);
     }

     // render the success view
}

올바른 논리 자체를 수행하거나 정보를 사용하여 모델을 호출하기 위해 올바른 IUserAction 인스턴스와 IUserAction 인스턴스를 얻기 위해 ModelBinder에 의존하고 있기 때문에 게시 작업을 상당히 일반적으로 만듭니다.

3 계층 환경에있는 경우 IUserAction은 경계를 가로 질러 촬영하고 앱 계층에서 유사한 방법으로 수행 할 간단한 DTO로 만들 수 있습니다. 해당 레이어를 수행하는 방법에 따라 매우 쉽게 분할되어 트랜잭션에 남아있을 수 있습니다 (Agatha의 요청 / 응답과 DI 및 NHibernate의 ID 맵을 활용하는 것입니다).

어쨌든 완벽한 아이디어는 아니라고 확신합니다. 관리하려면 클라이언트 측에서 JS가 필요하고 아직 프로젝트가 어떻게 진행되는지 확인하기 위해 프로젝트를 수행 할 수 없었지만 게시물은 방법에 대해 생각하려고했습니다. 거기에 다시 돌아와서 나는 내 생각을 줄 것이라고 생각했습니다. 도움이 되었기를 바라며 상호 작용을 관리하는 다른 방법에 대해 듣고 싶습니다.


흥미 롭군. ViewModel과 EditModel의 차이점과 관련하여 ... 편집 기능의 경우 ViewModel을 사용하여 양식을 만든 다음 사용자가 게시했을 때 EditModel에 바인딩한다고 제안합니까? 그렇다면 유효성 검사 오류로 인해 양식을 다시 게시해야하는 상황 (예 : ViewModel에 드롭 다운을 채우기위한 요소가 포함 된 경우)을 어떻게 처리 할 수 ​​있습니까? 드롭 다운 요소도 EditModel에 포함할까요? 어떤 경우에 둘의 차이점은 무엇입니까?
UpTheCreek

나는 EditModel을 사용하고 오류가 있으면 매우 비쌀 수있는 내 ViewModel을 다시 빌드해야한다는 우려가 있다고 생각합니다. ViewModel을 다시 빌드하고 사용자 알림 메시지 (유효성 검사 오류와 같은 긍정적 인 메시지와 부정적인 메시지 모두)를 넣을 장소가 있는지 확인합니다. 성능 문제로 판명되면 해당 세션의 다음 요청이 종료 될 때까지 항상 ViewModel을 캐시 할 수 있습니다 (아마도 EditModel의 게시물 일 수 있음).
Sean Copenhaver 2010 년

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.