code

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

codestyles 2020. 9. 17. 07:56
반응형

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


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

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

삽입 작업으로합시다. 동일한 Automapper를 사용할 수 있습니다. 하지만 업데이트 작업이라면 어떨까요? Repository에서 Domain Entity를 검색하고 ViewModel의 값에 따라 속성을 업데이트 한 다음 Repository에 저장해야합니다.

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


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

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

나는 사용 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도 제공됩니다.


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에서 수행됩니다 (사용자 지정 바인딩으로 확장 가능).
  • Error handling (i.e. catching data access exception throws by Repository) can be done by [HandleError] filter

Controller action is pretty thin and concerns are separated: mapping issues are addressed in AutoMapper configuration, validation is done by ModelBinder and data access by Repository.


I would like to say that you reuse the term ViewModel for both directions of the client interaction. If you have read enough ASP.NET MVC code in the wild you have probably seen the distinction between a ViewModel and an EditModel. I think that is important.

A ViewModel represents all the information required to render a view. This could include data that is rendered in static non-interactive places and also data purely to perform a check to decide on what exactly to render. A Controller GET action is generally responsible for packaging up the ViewModel for its View.

An EditModel (or perhaps an ActionModel) represents the data required to perform the action the user wanted to do for that POST. So an EditModel is really trying to describe an action. This will probably exclude some data from the ViewModel and although related I think it's important to realize they are indeed different.

One Idea

That said you could very easily have an AutoMapper configuration for going from Model -> ViewModel and a different one to go from EditModel -> Model. Then the different Controller actions just need to use AutoMapper. Hell the EditModel could have a functions on it to validate it's properties against the model and to apply those values to the Model itself. It's not doing anything else and you have ModelBinders in MVC to map the Request to the EditModel anyway.

Another Idea

Beyond that something I have been thinking about recently that sort of works off the idea of an ActionModel is that what the client is posting back to you is actually the description of several actions the user performed and not just one big glob of data. This would certainly require some Javascript on the client side to manage but the idea is intriguing I think.

Essentially as the user performs actions on the screen you have presented them, Javascript would start create a list of action objects. An example is possibly the user is at an employee information screen. They update the last name and add a new address because the employee has recently been married. Under the covers this produces a ChangeEmployeeName and an AddEmployeeMailingAddress objects to a list. The user clicks 'Save' to commit the changes and you submit the list of two objects, each containing just the information needed to perform each action.

You would need a more intelligent ModelBinder then the default one but good JSON serializer should be able to take care of the mapping of the client side action objects to the server side ones. The server side ones (if you are in a 2-tier environment) could easily have methods that completed the action on the Model they work with. So the Controller action ends up just getting an Id for the Model instance to pull and a list of actions to perform on it. Or the actions have the id in them to keep them very separate.

So maybe something like this gets realized on the server side:

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
}

That really makes the posting back action fairly generic since you are relying on your ModelBinder to get you the correct IUserAction instance and your IUserAction instance to either perform the correct logic itself or (more likely) call into the Model with the info.

If you were in a 3 tier environment the IUserAction could just be made simple DTOs to be shot across the boundary and performed in a similar method on the app layer. Depending on how you do that layer it could be split up very easily and still remain in a transaction (what comes to mind is Agatha's request/response and taking advantage of DI and NHibernate's identity map).

Anyway I'm sure it's not a perfect idea, it would require some JS on client side to manage, and I haven't been able to do a project yet to see how it unfolds, but the post was trying to think about how to get there and back again so I figured I would give my thoughts. I hope it helps and I would love to hear of other ways to manage the interactions.


You don't need mapping viewmodel to domain because your viewmodel may be created more than domain model. Viewmodels optimized for screen (ui) and different from domain model.

http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/

참고URL : https://stackoverflow.com/questions/2206005/how-to-map-view-model-back-to-domain-model-in-a-post-action

반응형