IT story

ViewModel 모범 사례

hot-time 2020. 4. 13. 08:21
반응형

ViewModel 모범 사례


에서 이 질문에 , 그것은 컨트롤러가 만들도록하는 것이 합리적 것 같습니다 뷰 모델 보다 정확하게보기를 표시하려고하고있는 모델을 반영하지만, 나는 (내가 MVC 패턴에 새로운 해요 규칙의 일부에 대해 궁금 해요 아직 명확하지 않은 경우).

기본적으로 다음과 같은 질문이 있습니다.

  1. 나는 보통 하나의 클래스 / 파일을 갖고 싶습니다. 로모그래퍼이 메이크업 감각을합니까 뷰 모델 이 만 볼 수있는 컨트롤러에서 데이터 떨어져 손에 작성되는 경우?
  2. 경우 뷰 모델은 자체 파일에 속하지 않는, 당신은, 일을 분리 유지하기 위해 디렉토리 / 프로젝트 구조를 사용하는 곳 수행 뷰 모델에 속하는 파일을? 에서 컨트롤러 디렉토리?

그것은 기본적으로 지금입니다. 몇 가지 질문이 더 나올 수도 있지만 지난 한 시간 동안 저를 괴롭 히고 다른 곳에서 일관된 지침을 찾을 수 있습니다.

편집 : CodePlex 의 샘플 NerdDinner 앱 을 보면 ViewModels가 Controllers의 일부인 것처럼 보이지만 여전히 자체 파일에 있지 않은 것이 불편합니다.


각 뷰에 대해 "ViewModel"이라고하는 것을 만듭니다. MVC 웹 프로젝트의 ViewModels 폴더에 넣었습니다. 컨트롤러와 액션 (또는 뷰)의 이름을 따서 이름을 지정합니다. 따라서 멤버쉽 컨트롤러의 SignUp보기에 데이터를 전달해야하는 경우 MembershipSignUpViewModel.cs 클래스를 만들어 ViewModels 폴더에 넣습니다.

그런 다음 컨트롤러에서보기로 데이터를 쉽게 전송할 수 있도록 필요한 속성과 방법을 추가합니다. Automapper를 사용하여 ViewModel에서 도메인 모델로 이동하고 필요한 경우 다시 돌아옵니다.

이것은 다른 ViewModel 유형의 속성을 포함하는 복합 ViewModel에도 효과적입니다. 예를 들어 멤버쉽 컨트롤러의 인덱스 페이지에 5 개의 위젯이 있고 각 부분 뷰에 대해 ViewModel을 만든 경우 인덱스 작업에서 부분으로 데이터를 어떻게 전달합니까? MyPartialViewModel 유형의 MembershipIndexViewModel에 속성을 추가하고 부분을 렌더링 할 때 Model.MyPartialViewModel에 전달합니다.

이렇게하면 인덱스 뷰를 전혀 변경하지 않고도 부분 ViewModel 속성을 조정할 수 있습니다. 여전히 Model.MyPartialViewModel을 전달하기 때문에 부분 ViewModel에 속성을 추가하는 것이 무엇이든 할 때 무언가를 해결하기 위해 전체 부분 체인을 거치지 않아도됩니다.

또한 네임 스페이스 "MyProject.Web.ViewModels"를 web.config에 추가하여 각 뷰에 명시적인 import 문을 추가하지 않고도 모든 뷰에서 참조 할 수 있도록합니다. 좀 더 깨끗하게 만듭니다.


범주 (컨트롤러, 뷰 모델, 필터 등)별로 클래스를 분리하는 것은 의미가 없습니다.

웹 사이트의 홈 섹션 (/)에 대한 코드를 작성하려면 Home이라는 폴더를 만들고 HomeController, IndexViewModel, AboutViewModel 등 및 홈 작업에 사용되는 모든 관련 클래스를 저장하십시오.

ApplicationController와 같은 클래스를 공유 한 경우 프로젝트의 루트에 놓을 수 있습니다.

왜 관련이있는 것들 (HomeController, IndexViewModel)을 분리하고 전혀 관련이없는 것들 (HomeController, AccountController)을 함께 유지해야 하는가?


이 주제에 관한 블로그 게시물을 작성했습니다 .


응용 프로그램 클래스를 "Core"(또는 별도의 클래스 라이브러리)라는 하위 폴더에 보관하고 KIGG 샘플 응용 프로그램 과 동일한 방법을 사용 하지만 응용 프로그램을 더 건조하게 만들기 위해 약간의 변경이 있습니다.

일반적인 사이트 전체 속성을 저장하는 / Core / ViewData /에 BaseViewData 클래스를 만듭니다.

이 후에도 동일한 폴더에 모든 View ViewData 클래스를 만든 다음 BaseViewData에서 파생하여 특정 뷰 속성을 갖습니다.

그런 다음 모든 컨트롤러에서 파생되는 ApplicationController를 만듭니다. ApplicationController에는 다음과 같은 일반적인 GetViewData 메소드가 있습니다.

protected T GetViewData<T>() where T : BaseViewData, new()
    {
        var viewData = new T
        {
           Property1 = "value1",
           Property2 = this.Method() // in the ApplicationController
        };
        return viewData;
    }

마지막으로 내 컨트롤러 작업에서 ViewData 모델을 빌드하기 위해 다음을 수행합니다.

public ActionResult Index(int? id)
    {
        var viewData = this.GetViewData<PageViewData>();
        viewData.Page = this.DataContext.getPage(id); // ApplicationController
        ViewData.Model = viewData;
        return View();
    }

나는 이것이 정말로 잘 작동한다고 생각하고 당신의 견해를 깔끔하게 유지하고 컨트롤러는 마른 체형을 유지합니다.


ViewModel 클래스는 클래스의 인스턴스로 표현되는 여러 데이터를 하나의 관리하기 쉬운 객체로 캡슐화하여 View에 전달할 수 있습니다.

ViewModel 클래스를 자체 파일의 자체 디렉토리에 두는 것이 좋습니다. 내 프로젝트에는 ViewModels라는 Models 폴더의 하위 폴더가 있습니다. 그것이 내 ViewModels (예 :)가 ProductViewModel.cs사는 곳입니다.


모델을 보관할 적절한 장소는 없습니다. 프로젝트가 크고 ViewModel (데이터 전송 객체)이 많은 경우 별도의 어셈블리로 유지할 수 있습니다. 또한 사이트 프로젝트의 별도 폴더에 보관할 수 있습니다. 예를 들어, Oxite 에서는 많은 클래스가 포함 된 Oxite 프로젝트에 배치됩니다. Oxite의 컨트롤러는 별도의 프로젝트로 이동하고 뷰도 별도의 프로젝트에 있습니다.
에서 CodeCampServer ViewModels *이 양식 명명 된 그들은 모델 폴더에 UI 프로젝트에 배치됩니다. MvcPress 프로젝트
에서는 데이터베이스와 함께 작동하는 모든 코드와 조금 더 포함 된 데이터 프로젝트에 배치됩니다 (그러나이 접근법은 권장하지 않습니다. 단지 샘플입니다)
많은 관점이 있습니다. 일반적으로 사이트 프로젝트에 내 ViewModel (DTO 객체)을 유지합니다. 그러나 10 개가 넘는 모델이있는 경우 별도의 어셈블리로 모델을 옮기는 것을 선호합니다. 일반적 으로이 경우 컨트롤러를 분리하여 어셈블리를 분리합니다.
또 다른 질문은 모델의 모든 데이터를 ViewModel에 쉽게 매핑하는 방법입니다. AutoMapper 라이브러리를 살펴 보는 것이 좋습니다 . 나는 그것을 매우 좋아한다, 그것은 나를 위해 모든 더러운 일을한다.
또한 SharpArchitecture 프로젝트 를 살펴볼 것을 제안합니다 . 프로젝트를위한 매우 훌륭한 아키텍처를 제공하며 멋진 프레임 워크와 지침 및 훌륭한 커뮤니티가 많이 포함되어 있습니다.


모범 사례의 코드 스 니펫은 다음과 같습니다.

    public class UserController : Controller
    {
        private readonly IUserService userService;
        private readonly IBuilder<User, UserCreateInput> createBuilder;
        private readonly IBuilder<User, UserEditInput> editBuilder;

        public UserController(IUserService userService, IBuilder<User, UserCreateInput> createBuilder, IBuilder<User, UserEditInput> editBuilder)
        {
            this.userService = userService;
            this.editBuilder = editBuilder;
            this.createBuilder = createBuilder;
        }

        public ActionResult Index(int? page)
        {
            return View(userService.GetPage(page ?? 1, 5));
        }

        public ActionResult Create()
        {
            return View(createBuilder.BuildInput(new User()));
        }

        [HttpPost]
        public ActionResult Create(UserCreateInput input)
        {
            if (input.Roles == null) ModelState.AddModelError("roles", "selectati macar un rol");

            if (!ModelState.IsValid)
                return View(createBuilder.RebuildInput(input));

            userService.Create(createBuilder.BuilEntity(input));
            return RedirectToAction("Index");
        }

        public ActionResult Edit(long id)
        {
            return View(editBuilder.BuildInput(userService.GetFull(id)));
        }

        [HttpPost]
        public ActionResult Edit(UserEditInput input)
        {           
            if (!ModelState.IsValid)
                return View(editBuilder.RebuildInput(input));

            userService.Save(editBuilder.BuilEntity(input));
            return RedirectToAction("Index");
        }
}

모든 ViewModel을 Models 폴더에 넣습니다 (모든 비즈니스 로직은 별도의 ServiceLayer 프로젝트에 있음)


개인적으로 ViewModel이 사소한 것이 아닌 경우 별도의 클래스를 사용하는 것이 좋습니다.

하나 이상의 뷰 모델이있는 경우 적어도 디렉토리에서 분할하는 것이 좋습니다. 뷰 모델이 나중에 공유되면 디렉토리에 내재 된 네임 스페이스를 통해 새 어셈블리로 쉽게 이동할 수 있습니다.


이 경우 뷰와 별개의 프로젝트에 모델과 컨트롤러가 있습니다.

경험상, 대부분의 ViewData [ "..."] 항목을 ViewModel로 옮기고 피하려고 시도했습니다. 따라서 캐스팅과 마법 문자열을 피하는 것이 좋습니다.

ViewModel은 또한 빵 부스러기와 제목을 그리는 페이지의 목록 정보 또는 페이지 정보에 대한 페이지 매김 정보와 같은 일반적인 속성을 보유합니다. 현재 기본 클래스는 내 의견으로는 너무 많은 정보를 보유하고 있으며 기본 뷰 모델 페이지의 99 %에 대해 가장 기본적이고 필요한 정보 인 3 가지 조각으로 나눈 다음 목록 및 모델의 모델로 나눌 수 있습니다. 해당 시나리오에 대한 특정 데이터를 보유하고 기본 시나리오에서 상속되는 양식의 경우

마지막으로 각 엔터티가 특정 정보를 처리 할 수있는 뷰 모델을 구현합니다.


컨트롤러의 코드 :

    [HttpGet]
        public ActionResult EntryEdit(int? entryId)
        {
            ViewData["BodyClass"] = "page-entryEdit";
            EntryEditViewModel viewMode = new EntryEditViewModel(entryId);
            return View(viewMode);
        }

    [HttpPost]
    public ActionResult EntryEdit(Entry entry)
    {
        ViewData["BodyClass"] = "page-entryEdit";            

        #region save

        if (ModelState.IsValid)
        {
            if (EntryManager.Update(entry) == 1)
            {
                return RedirectToAction("EntryEditSuccess", "Dictionary");
            }
            else
            {
                return RedirectToAction("EntryEditFailed", "Dictionary");
            }
        }
        else
        {
            EntryEditViewModel viewModel = new EntryEditViewModel(entry);
            return View(viewModel);
        }

        #endregion
    }

뷰 모델의 코드 :

public class EntryEditViewModel
    {
        #region Private Variables for Properties

        private Entry _entry = new Entry();
        private StatusList _statusList = new StatusList();        

        #endregion

        #region Public Properties

        public Entry Entry
        {
            get { return _entry; }
            set { _entry = value; }
        }

        public StatusList StatusList
        {
            get { return _statusList; }
        }

        #endregion

        #region constructor(s)

        /// <summary>
        /// for Get action
        /// </summary>
        /// <param name="entryId"></param>
        public EntryEditViewModel(int? entryId)
        {
            this.Entry = EntryManager.GetDetail(entryId.Value);                 
        }

        /// <summary>
        /// for Post action
        /// </summary>
        /// <param name="entry"></param>
        public EntryEditViewModel(Entry entry)
        {
            this.Entry = entry;
        }

        #endregion       
    }

프로젝트 :

  • DevJet.Web (ASP.NET MVC 웹 프로젝트)

  • DevJet.Web.App.Dictionary (별도의 클래스 라이브러리 프로젝트)

    이 프로젝트에서 DAL, BLL, BO, VM (뷰 모델 용 폴더)과 같은 폴더를 만들었습니다.


작업 결과 및 상황 별 데이터와 같이 일반적으로 필요한 속성이있는 뷰 모델 기본 클래스를 작성하고 현재 사용자 데이터 및 역할을 넣을 수도 있습니다

class ViewModelBase 
{
  public bool HasError {get;set;} 
  public string ErrorMessage {get;set;}
  public List<string> UserRoles{get;set;}
}

기본 컨트롤러 클래스에는 PopulateViewModelBase ()와 같은 메소드가 있으며이 메소드는 컨텍스트 데이터 및 사용자 역할을 채 웁니다. HasError 및 ErrorMessage는 service / db에서 데이터를 가져 오는 동안 예외가있는 경우 이러한 특성을 설정하십시오. 이러한 속성을보기에 바인딩하여 오류를 표시하십시오. 사용자 역할을 사용하여 역할을 기반으로보기에 숨기기 섹션을 표시 할 수 있습니다.

다른 get 액션으로 뷰 모델을 채우려면 추상 메소드 FillModel을 사용하여 기본 컨트롤러를 사용하여 뷰 모델을 일관성있게 만들 수 있습니다.

class BaseController :BaseController 
{
   public PopulateViewModelBase(ViewModelBase model) 
{
   //fill up common data. 
}
abstract ViewModelBase FillModel();
}

컨트롤러에서

class MyController :Controller 
{

 public ActionResult Index() 
{
   return View(FillModel()); 
}

ViewModelBase FillModel() 
{ 
    ViewModelBase  model=;
    string currentAction = HttpContext.Current.Request.RequestContext.RouteData.Values["action"].ToString(); 
 try 
{ 
   switch(currentAction) 
{  
   case "Index": 
   model= GetCustomerData(); 
   break;
   // fill model logic for other actions 
}
}
catch(Exception ex) 
{
   model.HasError=true;
   model.ErrorMessage=ex.Message;
}
//fill common properties 
base.PopulateViewModelBase(model);
return model;
}
}

참고 URL : https://stackoverflow.com/questions/664205/viewmodel-best-practices

반응형