MVC의 ViewModel은 무엇입니까?


429

ASP.NET MVC를 처음 사용합니다. ViewModel의 목적을 이해하는 데 문제가 있습니다.

ViewModel이란 무엇이며 왜 ASP.NET MVC 응용 프로그램에 ViewModel이 필요합니까?

그것의 작동과 설명에 대해 좋은 예를 얻으면 더 좋을 것입니다.


4
이 게시물은 당신이 찾는 것입니다- "ASP.NET MVC ViewModel은 무엇입니까?"
Yusubov 2016 년

6
이 기사는 멋지게 보입니다 : rachelappel.com/…
Andrew

답변:


607

A view model는 정적 텍스트 또는 데이터베이스에 추가하거나 편집 할 수있는 입력 값 (예 : 텍스트 상자 및 드롭 다운 목록)에 상관없이보기 / 페이지에 표시하려는 데이터를 나타냅니다. 당신과 다른 것 domain model입니다. 보기의 모델입니다.

Employee직원 도메인 모델을 나타내는 클래스가 있고 다음과 같은 속성 (고유 식별자, 이름, 성 및 생성 날짜)이 포함되어 있다고 가정하겠습니다 .

public class Employee : IEntity
{
     public int Id { get; set; }

     public string FirstName { get; set; }

     public string LastName { get; set; }

     public DateTime DateCreated { get; set; }
}

뷰 모델은 뷰에 사용하려는 데이터 (속성으로 표시) 만 포함한다는 점에서 도메인 모델과 다릅니다. 예를 들어, 새 직원 레코드를 추가한다고 가정하면 뷰 모델은 다음과 같습니다.

public class CreateEmployeeViewModel
{
     public string FirstName { get; set; }

     public string LastName { get; set; }
}

보시다시피 두 가지 속성 만 포함합니다. 이 두 속성은 직원 도메인 모델에도 있습니다. 왜 이런 질문을 할 수 있습니까? Id뷰에서 설정되지 않은 경우 Employee 테이블에 의해 자동 생성 될 수 있습니다. 또한 DateCreated스토어드 프로 시저 또는 애플리케이션의 서비스 계층에서 설정 될 수도 있습니다. 그래서 IdDateCreated뷰 모델에서 필요하지 않습니다. 직원의 세부 사항 (이미 캡처 된 직원)을 정적 텍스트로 볼 때이 두 특성을 표시 할 수 있습니다.

보기 / 페이지를로드 할 때 직원 컨트롤러에서 활동 작성 메소드는이보기 모델의 인스턴스를 작성하고 필요한 경우 필드를 채운 후이보기 모델을보기 / 페이지로 전달합니다.

public class EmployeeController : Controller
{
     private readonly IEmployeeService employeeService;

     public EmployeeController(IEmployeeService employeeService)
     {
          this.employeeService = employeeService;
     }

     public ActionResult Create()
     {
          CreateEmployeeViewModel model = new CreateEmployeeViewModel();

          return View(model);
     }

     public ActionResult Create(CreateEmployeeViewModel model)
     {
          // Do what ever needs to be done before adding the employee to the database
     }
}

뷰 / 페이지는 다음과 같이 보일 수 있습니다 (사용 ASP.NET MVC하고 있다고 가정 하고 Razor뷰 엔진).

@model MyProject.Web.ViewModels.CreateEmployeeViewModel

<table>
     <tr>
          <td><b>First Name:</b></td>
          <td>@Html.TextBoxFor(m => m.FirstName, new { maxlength = "50", size = "50" })
              @Html.ValidationMessageFor(m => m.FirstName)
          </td>
     </tr>
     <tr>
          <td><b>Last Name:</b></td>
          <td>@Html.TextBoxFor(m => m.LastName, new { maxlength = "50", size = "50" })
              @Html.ValidationMessageFor(m => m.LastName)
          </td>
     </tr>
</table>

따라서 FirstName및에 대해서만 유효성 검사가 수행됩니다 LastName. FluentValidation 을 사용 하면 다음과 같은 유효성 검사가 가능합니다.

public class CreateEmployeeViewModelValidator : AbstractValidator<CreateEmployeeViewModel>
{
     public CreateEmployeeViewModelValidator()
     {
          RuleFor(m => m.FirstName)
               .NotEmpty()
               .WithMessage("First name required")
               .Length(1, 50)
               .WithMessage("First name must not be greater than 50 characters");

          RuleFor(m => m.LastName)
               .NotEmpty()
               .WithMessage("Last name required")
               .Length(1, 50)
               .WithMessage("Last name must not be greater than 50 characters");
     }
}

그리고 데이터 주석을 사용하면 다음과 같이 보일 수 있습니다.

public class CreateEmployeeViewModel : ViewModelBase
{
    [Display(Name = "First Name")]
    [Required(ErrorMessage = "First name required")]
    public string FirstName { get; set; }

    [Display(Name = "Last Name")]
    [Required(ErrorMessage = "Last name required")]
    public string LastName { get; set; }
}

기억해야 할 핵심은 뷰 모델이 사용하려는 데이터만을 나타내며 , 그 밖의 다른 것은 없다는 것입니다. 30 개의 속성을 가진 도메인 모델이 있고 단일 값만 업데이트하려는 경우 불필요한 모든 코드와 유효성 검사를 상상할 수 있습니다. 이 시나리오에서는 도메인 모델에있는 모든 속성이 아니라보기 모델에이 값 / 속성 만 있습니다.

뷰 모델에는 하나의 데이터베이스 테이블의 데이터 만있을 수 있습니다. 다른 테이블의 데이터를 결합 할 수 있습니다. 새 직원 레코드 추가에 대한 위의 예를 들어보십시오. 성과 이름을 추가하는 것 외에도 직원 부서를 추가 할 수도 있습니다. 이 부서 목록은 Departments테이블 에서 가져옵니다 . 이제 하나의 뷰 모델에 EmployeesDepartments테이블의 데이터가 있습니다. 그런 다음 뷰 모델에 다음 두 속성을 추가하고 데이터로 채워야합니다.

public int DepartmentId { get; set; }

public IEnumerable<Department> Departments { get; set; }

직원 데이터 (데이터베이스에 이미 추가 된 직원)를 편집 할 때 위의 예와 크게 다르지 않습니다. 뷰 모델을 작성하십시오 (예 :) EditEmployeeViewModel. 이 뷰 모델에서 편집하려는 데이터 (이름 및 성) 만 있습니다. 데이터를 편집하고 제출 버튼을 클릭하십시오. 값이 URL에있을 Id것이므로 필드 에 대해 너무 걱정하지 않아도 Id됩니다. 예를 들면 다음과 같습니다.

http://www.yourwebsite.com/Employee/Edit/3

이것을 가지고 Id이름과 성 값과 함께 저장소 계층으로 전달하십시오.

레코드를 삭제할 때 일반적으로 편집 뷰 모델과 동일한 경로를 따릅니다. 예를 들어 URL도 있습니다.

http://www.yourwebsite.com/Employee/Delete/3

뷰가 처음로드되면 Idof 3을 사용하여 데이터베이스에서 직원의 데이터를 가져옵니다. 그런 다음 사용자가 삭제중인 직원을 볼 수 있도록보기 / 페이지에 정적 텍스트를 표시합니다. 사용자가 삭제 버튼을 클릭하면 Id3 의 값을 사용하여 내 저장소 계층에 전달합니다. 당신은 단지 필요 Id테이블에서 레코드를 삭제합니다.

또 다른 요점으로, 모든 액션에 대해 뷰 모델이 실제로 필요하지는 않습니다. 간단한 데이터라면을 사용하는 것이 좋습니다 EmployeeViewModel. 복잡한 뷰 / 페이지이고 서로 다르면 각각에 대해 별도의 뷰 모델을 사용하는 것이 좋습니다.

이것이 뷰 모델과 도메인 모델에 대한 혼란을 없애기를 바랍니다.


5
@ 케니 : 그런 다음 그것을 보여줍니다 :) 내가 말하려는 것은 50 개의 속성을 가진 도메인 모델이 있고 뷰는 5 만 표시하면 5를 표시하기 위해 50 개의 속성을 모두 보내는 데는 사용하지 않는다는 것입니다.
Brendan Vogt

5
@BrendanVogt – 당신은 그것을 잘 설명했지만, "50 개의 속성을 모두 보내는"비용이 얼마인지 이해하지 못합니다. 다른 코드는 이미 50 개의 속성이있는 Model 객체를 만들었으며 45 개의 속성을 보내지 않기 위해 다른 클래스를 유지하는 것이 바람직하지 않습니다. 특히 나중에 45 개의 속성 중 하나를 보내려 경우에는 더욱 그렇습니다 .
Kenny Evitt

5
@BrendanVogt – 아마도 LukLed의 대답은 왜 이것이 유용한 지 이해하는 데 도움이된다고 생각합니다. 특히 ViewModel은 "다른 데이터베이스 엔터티의 값을 결합 할 수 있습니다." 데이터베이스 개체 "를"모델 개체 "로 대체합니다.]. 그러나 여전히 ViewModels는 어떤 특정 문제를 해결하려고 했습니까? 당신은 어떤 링크가 있습니까? 나는 아무것도 찾을 수 없었다. [내가 당신을 고르는 것 같다면 사과드립니다!]
Kenny Evitt

1
방금 ViewModels가 여러 컬렉션 (또는 교차 모델 속성)을 viewBag에 넣지 않고도 단일 뷰로 보내는 좋은 방법이라고 말하는 것을 들었습니다. 이해가 되네요
Ayyash

3
비판적으로 죄송하지만이 답변은 불완전합니다. 페이지에 표시 할 항목만으로 뷰 모델을 정의하는 것은 "차란 무엇입니까?" "비행기가 아닙니다"라는 답변을받습니다. 사실이지만 그다지 도움이되지는 않습니다. VM의보다 정확한 정의는 "페이지를 렌더링하는 데 필요한 모든 것"입니다. 하단을 읽으면 기존 도메인 모델과 프레젠테이션 모델을 활용하여 VM을 정확하고 쉽게 구축하는 데 필요한 구성 요소를 식별했습니다.
Sam

133

뷰 모델 은 특정 뷰에서 사용되는 데이터 모델을 나타내는 클래스입니다. 이 클래스를 로그인 페이지의 모델로 사용할 수 있습니다.

public class LoginPageVM
{
    [Required(ErrorMessage = "Are you really trying to login without entering username?")]
    [DisplayName("Username/e-mail")]
    public string UserName { get; set; }
    [Required(ErrorMessage = "Please enter password:)")]
    [DisplayName("Password")]
    public string Password { get; set; }
    [DisplayName("Stay logged in when browser is closed")]
    public bool RememberMe { get; set; }
}

이 뷰 모델을 사용하여 뷰를 정의 할 수 있습니다 (Razor 뷰 엔진).

@model CamelTrap.Models.ViewModels.LoginPageVM

@using (Html.BeginForm()) {
    @Html.EditorFor(m => m);
    <input type="submit" value="Save" class="submit" />
}

그리고 행동 :

[HttpGet]
public ActionResult LoginPage()
{
    return View();
}

[HttpPost]
public ActionResult LoginPage(LoginPageVM model)
{
    ...code to login user to application...
    return View(model);
}

이 결과를 생성합니다 (유효성 검사 메시지와 함께 양식을 제출 한 후 화면이 표시됨).

보시다시피 뷰 모델에는 많은 역할이 있습니다.

  • 뷰 모델은 뷰로 표시되는 필드 만 구성하여 뷰를 문서화합니다.
  • 뷰 모델에는 데이터 주석 또는 IDataErrorInfo를 사용하는 특정 유효성 검사 규칙이 포함될 수 있습니다.
  • 보기 모델은 뷰 (찾는 방법을 정의 LabelFor, EditorFor, DisplayFor헬퍼).
  • 뷰 모델은 다른 데이터베이스 엔터티의 값을 결합 할 수 있습니다.
  • DisplayFor 또는 EditorFor 도우미를 사용하여 뷰 모델에 대한 표시 템플릿을 쉽게 지정하고 여러 곳에서 재사용 할 수 있습니다.

뷰 모델 및 검색의 다른 예 : 기본 사용자 데이터, 그의 권한 및 사용자 이름을 표시하려고합니다. 필수 필드 만 포함하는 특수 뷰 모델을 만듭니다. 데이터베이스에서 다른 엔티티에서 데이터를 검색하지만 뷰는 뷰 모델 클래스 만 인식합니다.

public class UserVM {
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public bool IsAdministrator { get; set; }
    public string MothersName { get; set; }
}

검색:

var user = db.userRepository.GetUser(id);

var model = new UserVM() {
   ID = user.ID,
   FirstName = user.FirstName,
   LastName = user.LastName,
   IsAdministrator = user.Proviledges.IsAdministrator,
   MothersName = user.Mother.FirstName + " " + user.Mother.LastName
} 

얇은 user.Mother.FirstName + ""+ user.Mother.LastName은 View Model End에서 수행해야합니다. 모든 로직은 View Model 끝에서 수행되어야합니다.
Kurkula

3
@Chandana : 뷰 모델에서 간단한 연결을 수행 할 수 있다고 생각합니다. 두 필드를 함께 표시해야하는 경우 두 필드를 공개 할 이유가 없습니다.
LukLed

82

편집 : 내 블로그 에서이 답변을 업데이트했습니다.

http://www.samwheat.com/post/The-function-of-ViewModels-in-MVC-web-development

내 대답은 약간 길지만 뷰 모델을 다른 유형과 일반적으로 사용되는 모델과 비교하여 왜 다른지, 왜 필요한지를 이해하는 것이 중요하다고 생각합니다.

요약 된 질문과 질문에 직접 대답하려면 :

일반적으로 뷰 모델은 뷰를 렌더링하는 데 필요한 모든 속성과 메서드가 포함 된 객체입니다. 뷰 모델 속성은 종종 고객 및 주문과 같은 데이터 객체와 관련이 있으며 사용자 이름, 애플리케이션 이름 등과 같은 페이지 또는 애플리케이션 자체와 관련된 속성도 포함합니다. 뷰 모델은 렌더링 엔진에 전달할 편리한 객체를 제공합니다. html 페이지를 만듭니다. 뷰 모델을 사용하는 많은 이유 중 하나는 뷰 모델이 사용자 입력 처리, 데이터 유효성 검사, 표시 할 데이터 검색 등과 같은 특정 프레젠테이션 작업을 단위로 테스트 할 수있는 방법을 제공하기 때문입니다.

다음은 엔터티 모델 (일명 DTO의 일명 모델), 프레젠테이션 모델 및 뷰 모델을 비교 한 것입니다.

"모델"이라는 데이터 전송 개체

DTO (데이터 전송 개체)는 데이터베이스의 테이블 스키마와 일치하는 속성이있는 클래스입니다. DTO는 데이터 저장소와 데이터를주고받는 데 일반적으로 사용됩니다.
DTO의 특징 :

• 비즈니스 개체 – 정의는 응용 프로그램 데이터에 따라 다릅니다.

• 일반적으로 코드가없는 속성 만 포함합니다.

• 데이터베이스와의 데이터 전송에 주로 사용됩니다.

• 속성은 데이터 저장소의 특정 테이블에있는 필드와 정확히 또는 밀접하게 일치합니다.

데이터베이스 테이블은 일반적으로 정규화되므로 DTO도 일반적으로 정규화됩니다. 따라서 데이터 표시에 제한적으로 사용됩니다. 그러나 특정 간단한 데이터 구조의 경우 종종 잘 수행됩니다.

다음은 DTO의 모습에 대한 두 가지 예입니다.

public class Customer
{
    public int ID { get; set; }
    public string CustomerName { get; set; }
}


public class Order
{
    public int ID { get; set; }
    public int CustomerID { get; set; }
    public DateTime OrderDate { get; set; }
    public Decimal OrderAmount { get; set; }
}

프리젠 테이션 모델

프리젠 테이션 모델은 화면이나 보고서에서 데이터를 렌더링하는 데 사용되는 유틸리티 클래스입니다. 프리젠 테이션 모델은 일반적으로 여러 DTO의 데이터로 구성된 복잡한 데이터 구조를 모델링하는 데 사용됩니다. 프리젠 테이션 모델은 종종 비정규 화 된 데이터 뷰를 나타냅니다.

프리젠 테이션 모델의 특징 :

• 비즈니스 개체 – 정의는 응용 프로그램 데이터에 따라 다릅니다.

• 대부분 속성을 포함합니다. 코드는 일반적으로 데이터 형식을 지정하거나 DTO로 또는 DTO로 변환하는 것으로 제한됩니다. 프리젠 테이션 모델에는 비즈니스 로직이 포함되지 않아야합니다.

• 비정규 화 된 데이터보기를 제공하는 경우가 종종 있습니다. 즉, 종종 여러 DTO의 속성을 결합합니다.

• 종종 DTO와 다른 기본 유형의 속성을 포함합니다. 예를 들어 달러 금액은 문자열로 표시되어 쉼표와 통화 기호를 포함 할 수 있습니다.

• 객체 특성뿐만 아니라 사용 방법에 따라 정의되기도합니다. 다시 말해, 그리드를 렌더링하기위한 백킹 모델로 사용되는 간단한 DTO는 실제로 해당 그리드와 관련된 프레젠테이션 모델이기도합니다.

프리젠 테이션 모델은“필요한”및“필요한 위치”(DTO는 일반적으로 데이터베이스 스키마와 연결되어 있음)로 사용됩니다. 프리젠 테이션 모델은 전체 페이지, 페이지의 그리드 또는 페이지의 그리드 드롭 다운에 대한 데이터를 모델링하는 데 사용될 수 있습니다. 프리젠 테이션 모델에는 종종 다른 프리젠 테이션 모델 인 특성이 포함됩니다. 프리젠 테이션 모델은 종종 특정 그리드를 단일 페이지에 렌더링하는 것과 같은 단일 용도로 구성됩니다.

프레젠테이션 모델의 예 :

public class PresentationOrder
{
    public int OrderID { get; set; }
    public DateTime OrderDate { get; set; }
    public string PrettyDate { get { return OrderDate.ToShortDateString(); } }
    public string CustomerName { get; set; }
    public Decimal OrderAmount { get; set; }
    public string PrettyAmount { get { return string.Format("{0:C}", OrderAmount); } }
}

모델보기

뷰 모델은 뷰를 렌더링하기위한 백킹 클래스라는 점에서 프리젠 테이션 모델과 유사합니다. 그러나 프레젠테이션 모델 또는 DTO와는 구성 방식이 매우 다릅니다. 뷰 모델에는 프레젠테이션 모델 및 DTO와 동일한 속성이 포함되는 경우가 많기 때문에 종종 서로 혼동됩니다.

뷰 모델의 특징 :

• 페이지 또는 화면을 렌더링하는 데 사용되는 단일 데이터 소스입니다. 일반적으로 이것은 뷰 모델이 페이지의 모든 컨트롤이 올바르게 렌더링해야하는 모든 속성을 노출한다는 것을 의미합니다. 뷰 모델을 뷰의 단일 데이터 소스로 만들면 단위 테스트의 기능과 가치가 크게 향상됩니다.

• 응용 프로그램 코드로 사용되는 속성뿐만 아니라 응용 프로그램 데이터로 구성된 속성이 포함 된 복합 개체 입니다. 이 특성은 재사용 성을 위해 뷰 모델을 설계 할 때 중요하며 아래 예에서 설명합니다.

• 응용 프로그램 코드를 포함합니다. 뷰 모델에는 일반적으로 렌더링 중 및 사용자가 페이지와 상호 작용할 때 호출되는 메서드가 포함됩니다. 이 코드는 일반적으로 이벤트 처리, 애니메이션, 컨트롤의 가시성, 스타일 등에 관련됩니다.

• 데이터를 검색하거나 데이터베이스 서버로 전송하기 위해 비즈니스 서비스를 호출하는 코드를 포함합니다. 이 코드는 종종 컨트롤러에 실수로 배치됩니다. 컨트롤러에서 비즈니스 서비스를 호출하면 일반적으로 단위 테스트에 대한 뷰 모델의 유용성이 제한됩니다. 명확히하기 위해 뷰 모델 자체에는 비즈니스 로직이 포함되지 않아야하지만 비즈니스 로직이 포함 된 서비스를 호출해야합니다.

• 종종 다른 페이지 나 화면에 대한 다른 뷰 모델 인 속성이 포함되어 있습니다.

• "페이지 당"또는 "화면 당"으로 작성되었습니다. 고유 한 뷰 모델은 일반적으로 응용 프로그램의 모든 페이지 또는 화면에 대해 작성됩니다.

• 대부분의 페이지와 화면은 공통 속성을 공유하므로 일반적으로 기본 클래스에서 파생됩니다.

모델 구성보기

앞에서 언급 한 것처럼 뷰 모델은 단일 객체에서 응용 프로그램 속성과 비즈니스 데이터 속성을 결합한다는 점에서 복합 객체입니다. 뷰 모델에 사용되는 일반적으로 사용되는 응용 프로그램 속성의 예는 다음과 같습니다.

• 오류 메시지, 사용자 이름, 상태 등과 같은 응용 프로그램 상태를 표시하는 데 사용되는 속성

• 컨트롤의 서식, 표시, 스타일 또는 애니메이션에 사용되는 속성.

• 목록 개체와 같은 데이터 바인딩에 사용되는 속성 및 사용자가 입력 한 중간 데이터를 저장하는 속성.

다음 예는 뷰 모델의 복합 특성이 중요한 이유와 효율적이고 재사용 가능한 뷰 모델을 가장 잘 구성 할 수있는 방법을 보여줍니다.

웹 애플리케이션을 작성한다고 가정하십시오. 응용 프로그램 디자인의 요구 사항 중 하나는 페이지 제목, 사용자 이름 및 응용 프로그램 이름이 모든 페이지에 표시되어야한다는 것입니다. 프리젠 테이션 순서 오브젝트를 표시하기 위해 페이지를 작성하려는 경우 프리젠 테이션 모델을 다음과 같이 수정할 수 있습니다.

public class PresentationOrder
{
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }
    public int OrderID { get; set; }
    public DateTime OrderDate { get; set; }
    public string PrettyDate { get { return OrderDate.ToShortDateString(); } }
    public string CustomerName { get; set; }
    public Decimal OrderAmount { get; set; }
    public string PrettyAmount { get { return string.Format("{0:C}", OrderAmount); } }
}

이 디자인은 효과가있을 수 있지만 주문 목록을 표시 할 페이지를 만들려면 어떻게해야합니까? PageTitle, UserName 및 ApplicationName 속성이 반복되어 다루기 어려워집니다. 또한 클래스 생성자에서 일부 페이지 수준 논리를 정의하려면 어떻게해야합니까? 표시 될 모든 주문에 대해 인스턴스를 생성하면 더 이상 그렇게 할 수 없습니다.

상속에 대한 구성

다음은 주문 프리젠 테이션 모델이 실제 뷰 모델이되도록 단일 리팩터링하는 방법이며 단일 PresentationOrder 객체 또는 PresentationOrder 객체 컬렉션을 표시하는 데 유용합니다.

public class PresentationOrderVM
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }

    // Business properties
    public PresentationOrder Order { get; set; }
}


public class PresentationOrderVM
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }

    // Business properties
    public List<PresentationOrder> Orders { get; set; }
}

위의 두 클래스를 보면 뷰 모델을 생각하는 한 가지 방법은 다른 프리젠 테이션 모델을 속성으로 포함하는 프리젠 테이션 모델이라는 것을 알 수 있습니다. 최상위 프레젠테이션 모델 (예 : 뷰 모델)에는 페이지 또는 응용 프로그램과 관련된 속성이 포함되고 프레젠테이션 모델 (속성)에는 응용 프로그램 데이터와 관련된 속성이 포함됩니다.

디자인을 한 단계 더 발전시켜 PresentationOrders뿐만 아니라 다른 클래스에도 사용할 수있는 기본 뷰 모델 클래스를 만들 수 있습니다.

public class BaseViewModel
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }
}

이제 PresentationOrderVM을 다음과 같이 단순화 할 수 있습니다.

public class PresentationOrderVM : BaseViewModel
{
    // Business properties
    public PresentationOrder Order { get; set; }
}

public class PresentationOrderVM : BaseViewModel
{
    // Business properties
    public List<PresentationOrder> Orders { get; set; }
}

우리는 BaseViewModel을 일반화함으로써 더욱 재사용 가능하게 만들 수 있습니다 :

public class BaseViewModel<T>
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }

    // Business property
    public T BusinessObject { get; set; }
}

이제 우리의 구현은 수월합니다 :

public class PresentationOrderVM : BaseViewModel<PresentationOrder>
{
    // done!
}

public class PresentationOrderVM : BaseViewModel<List<PresentationOrder>>
{
    // done!
}

2
감사합니다 !! 이를 통해 다면적 엔티티 인 View-Model을 완전히 파악할 수있었습니다. 저는 MVC 아키텍처를 배우는 대학생입니다. 이것은 개발자에게 노출되는 많은 기능을 명확히했습니다. 내가 당신의 대답 옆에 별표를 표시 할 수 있다면.
Chef_Code

1
@Sam '뷰 모델은 종종 프리젠 테이션 모델 및 DTO와 동일한 속성을 포함하므로 이러한 이유로 종종 서로 혼동됩니다.' 이는 프리젠 테이션 모델 대신 일반적으로 사용 되거나 프리젠 테이션 모델 / dto를 포함한다는 의미입니까?
Alexander Derck가

2
@AlexanderDerck 다른 목적으로 사용됩니다. 그들은 다른 하나를 혼동합니다 (오류). 아니요, 일반적으로 뷰 모델 대신 프리 모델을 사용하지 않습니다. VM이 프리젠 테이션 모델을 "포함"한다는 것이 훨씬 일반적입니다. MyViewModel<MyPresModel>
Sam

2
@Sam 모델 객체가 nhibernate 모델과 같은 라이브 객체라고 가정합니다. 따라서 BusinessObject를 가짐으로써 모델 / 라이브 객체를 뷰에 직접 노출시키지 않습니까? 즉, 비즈니스 객체를 사용하여 데이터베이스 상태를 직접 수정할 수 있습니까? 또한 중첩 뷰 모델은 어떻습니까? 여러 비즈니스 개체 속성이 필요합니까?
Muhammad Ali

22

뷰와 관련된 속성이 있고 DB / Service / Data 저장소와 관련이없는 경우 ViewModels를 사용하는 것이 좋습니다. DB 필드 (또는 2 개)를 기준으로 확인란을 선택된 상태로 두려고하지만 DB 필드 자체는 부울이 아닙니다. 모델 자체에서 이러한 특성을 작성하고 데이터에 바인딩하지 않고 숨길 수는 있지만 해당 필드 및 트랜잭션의 양에 따라 모델을 어지럽히 지 않을 수 있습니다.

뷰 특정 데이터 및 / 또는 변환이 너무 적은 경우 모델 자체를 사용할 수 있습니다


19

나는 모든 게시물을 읽지는 않았지만 모든 답변에는 실제로 "얻을"수있는 한 가지 개념이 누락 된 것 같습니다 ...

모델이 데이터베이스 테이블 과 유사하면 ViewModel은 데이터베이스와 유사합니다. 보기 -일반적으로 뷰는 한 테이블에서 소량의 데이터를 반환하거나 여러 테이블 (조인)의 복잡한 데이터 집합을 반환합니다.

ViewModels를 사용하여 정보를보기 / 양식에 전달 한 다음 양식이 컨트롤러에 다시 게시 될 때 해당 데이터를 유효한 모델로 전송합니다 .Lists (IEnumerable)를 저장하는 데 매우 유용합니다.


11

MVC에는 뷰 모델이 없습니다. 모델, 뷰 및 컨트롤러가 있습니다. 뷰 모델은 MVVM (Model-View-Viewmodel)의 일부입니다. MVVM은 프레젠테이션 모델에서 파생되었으며 WPF에서 대중화되었습니다. MVVM에는 모델도 있어야하지만 대부분의 사람들은 해당 패턴의 요점을 완전히 놓치고 뷰와 뷰 모델 만 갖습니다. MVC의 모델은 MVVM의 모델과 유사합니다.

MVC에서 프로세스는 3 가지 책임으로 나뉩니다.

  • View는 사용자에게 데이터를 제공합니다.
  • 컨트롤러는 페이지 흐름을 담당합니다.
  • 비즈니스 로직을 책임지는 모델

MVC는 웹 응용 프로그램에 적합하지 않습니다. 데스크탑 응용 프로그램을 만들기 위해 Smalltalk에서 도입 한 패턴입니다. 웹 환경은 완전히 다릅니다. 40 년 된 개념을 데스크탑 개발에서 복사하여 웹 환경에 붙여 넣는 것은 의미가 없습니다. 그러나 많은 사람들이 응용 프로그램이 올바른 값을 컴파일하고 반환하기 때문에 이것이 정상이라고 생각합니다. 즉, 특정 디자인 선택을 괜찮다고 선언하기에 충분하지 않습니다.

웹 애플리케이션에서 모델의 예는 다음과 같습니다.

public class LoginModel
{
    private readonly AuthenticationService authentication;

    public LoginModel(AuthenticationService authentication)
    {
        this.authentication = authentication;
    }

    public bool Login()
    {
        return authentication.Login(Username, Password);
    }

    public string Username { get; set; }
    public string Password { get; set; }
}

컨트롤러는 다음과 같이 사용할 수 있습니다.

public class LoginController
{
    [HttpPost]
    public ActionResult Login(LoginModel model)
    {
        bool success = model.Login();

        if (success)
        {
            return new RedirectResult("/dashboard");
        }
        else
        {
            TempData["message"] = "Invalid username and/or password";
            return new RedirectResult("/login");
        }
    }
}

컨트롤러 방법과 모델은 작고 쉽게 테스트 할 수 있으며 문제가되지 않습니다.


MVVM 아키텍처에 대한 통찰력에 감사하지만 MVC가 왜 좋지 않습니까? 당신의 추론은 의심스럽고 편애적인 것으로 의심됩니다. MVVM에 대해서는 아무것도 모르지만 MVC와 같은 아키텍처가 50k 줄의 코드를 작성하지 않고도 동작을 모방 할 수 있다면 큰 문제는 무엇입니까?
Chef_Code

@Chef_Code : 의심 스럽거나 호의적이지 않습니다 : MVC에 관한 원본을 읽으십시오. 소스로 돌아가는 것은 의심없이 무리를 맹목적으로 따르는 것 (일명 "모범 사례")보다 훨씬 낫습니다. MVC는 훨씬 작은 단위를 의미합니다. 예를 들어 화면의 버튼은 모델, 뷰 및 컨트롤러로 구성됩니다. Web-MVC에서 전체 페이지에는 컨트롤러, 모델 및보기가 있습니다. 모델과 뷰가 연결되어 모델의 변경 사항 이 뷰에 즉시 반영되고 그 반대도 마찬가지입니다. 흉내 내기는 매우 중요합니다. 아키텍처는 개발자에게 거짓말해서는 안됩니다.
Jeroen

1
@jeroen 약어 MVC가 도난 당하고 엉망이되었습니다. 예 MVC에는 VM이 ​​없지만 리포지토리 또는 서비스 계층이 없으며 이러한 개체는 웹 사이트에서 널리 사용됩니다. OP가 "MVC에서 VM을 소개하고 사용하는 방법"을 묻고 있다고 생각합니다. MVC의 새로운 의미에서 모델은 비즈니스 로직이 속한 곳이 아닙니다. 비즈니스 로직은 MVC 또는 MVVM을 사용하는 웹 또는 데스크톱 앱의 서비스 계층에 속합니다. 모델이라는 용어는 서비스 계층으로 /로부터 전달되는 비즈니스 오브젝트를 설명합니다. 이러한 정의는 MVC의 원래 설명과 크게 다릅니다.
Sam

1
@Sam 웹 사이트의 일부인 MVC의 일부라고 할 수있는 것은 아닙니다. MVC의 새로운 의미는 없습니다. 올바른 의미와 "사람들이 MVC와 혼동하는 것과 완전히 관련이없는"의미가 있습니다. 모델이 비즈니스 로직을 담당한다고 말하면 비즈니스 로직이 모델에 코딩 된 것과 동일하지 않습니다. 대부분의 경우 모델은 응용 프로그램의 정면 역할을합니다.
Jeroen

Microsoft의 MVC에서 볼 수있는 주요 결함은 뷰가있는 모델을 잠그는 것입니다. 그것은 지난 20 년 동안 N-Tier 디자인에서 진행된이 분리의 모든 목적을 무너 뜨 렸습니다. 그들은 우리에게 2002 년 "WebForms"를 사용하도록 강요하는 시간을 허비했다. 이것은 웹에서 영감을 얻은 또 다른 데스크탑 기반 모델이었다. 이제 그들은 그것을 던져 버렸지 만 웹 개발자를위한이 새로운 패러다임에서 또 다른 데스크탑 모델을 다시 끌어 올렸습니다. 그동안 구글과 다른 사람들은 그것을 분리시키는 거대한 고객 측 모델을 만들고 있습니다. 1998 년 이후의 오래된 ASP VBScript는 최고의 웹 개발 시스템이라고 생각합니다.
Stokely

11

뷰 모델 a는 둘 이상의 클래스 속성을 포함 할 수있는 간단한 클래스입니다. 우리는 필요한 모든 속성을 상속하기 위해 사용합니다. 예를 들어 Student와 Subject라는 두 클래스가 있습니다.

Public class Student
{
public int Id {get; set;}
public string Name {get; set;}
}  
Public class Subject
{
public int SubjectID {get; set;}
public string SubjectName {get; set;}
}

이제 MVC에서 View에 학생의 이름과 과목의 이름을 기록하고 싶지만 다음과 같은 클래스를 두 개 이상 추가 할 수는 없습니다.

 @model ProjectName.Model.Student  
 @model ProjectName.Model.Subject

위의 코드는 오류가 발생합니다 ...

이제 우리는 하나의 클래스를 만들어 어떤 이름이든 지정할 수 있지만이 형식 "XyzViewModel"은 이해하기 쉽도록합니다. 상속 개념입니다. 이제 다음 이름으로 세 번째 클래스를 만듭니다.

public class StudentViewModel:Subject
{
public int ID {get; set;}
public string Name {get; set;}
}

이제 View에서이 ViewModel을 사용합니다

@model ProjectName.Model.StudentViewModel

이제 View에서 StudentViewModel 및 상속 된 클래스의 모든 속성에 액세스 할 수 있습니다.


10

많은 큰 예들이 명확하고 또렷하게 설명하겠습니다.

ViewModel = 뷰를 제공하기 위해 작성된 모델입니다.

ASP.NET MVC 뷰는 둘 이상의 모델을 가질 수 없으므로 둘 이상의 모델의 속성을 뷰에 표시해야하는 경우에는 불가능합니다. ViewModel이이 목적을 수행합니다.

뷰 모델은 뷰에 필요한 속성 만 보유 할 수있는 모델 클래스입니다. 또한 데이터베이스의 둘 이상의 엔티티 (테이블)의 특성을 포함 할 수 있습니다. 이름에서 알 수 있듯이이 모델은 View 요구 사항에 따라 만들어집니다.

뷰 모델의 몇 가지 예는 다음과 같습니다.

  • 뷰 페이지에 여러 엔터티의 데이터를 나열하려면 – 뷰 모델을 만들고 데이터를 나열하려는 모든 엔터티의 속성을 가질 수 있습니다. 해당 데이터베이스 엔터티에 가입하고 뷰 모델 속성을 설정하고 뷰로 돌아가서 다른 엔터티의 데이터를 하나의 표 형식으로 표시
  • 뷰 모델은 뷰에 필요한 단일 엔터티의 특정 필드 만 정의 할 수 있습니다.

ViewModel을 사용하여 여러 엔터티에 레코드를 삽입하고 업데이트 할 수도 있지만 ViewModel의 주요 용도는 여러 엔터티 (모델)의 열을 단일 뷰에 표시하는 것입니다.

ViewModel 작성 방법은 Model 작성 방법과 동일하며, Viewmodel보기 작성 방법은 Model보기 작성 방법과 동일합니다.

다음은 ViewModel을 사용 하는 List 데이터 의 작은 예입니다 .

이것이 도움이 되길 바랍니다.


6

ViewModel은 MVC 프레임 워크의 개념적 서투른 부분을 패치하는 해결 방법입니다. 3 계층 Model-View-Controller 아키텍처에서 4 번째 계층을 나타냅니다. 모델 (도메인 모델)이 적절하지 않고 View에 너무 큰 (2-3 개 필드보다 큼) View를 전달하기 위해 더 작은 ViewModel을 만듭니다.


1

뷰 모델은 데이터의 개념적 모델입니다. 예를 들어 서브 세트를 가져 오거나 다른 테이블에서 데이터를 결합하는 데 사용됩니다.

특정 속성 만 원할 수 있으므로 추가 속성이 아닌 속성 만로드 할 수 있습니다.


1
  • ViewModel에는 뷰에 표시되는 필드가 포함되어 있습니다 (LabelFor, EditorFor, DisplayFor 도우미)
  • ViewModel은 데이터 주석 또는 IDataErrorInfo를 사용하여 특정 유효성 검사 규칙을 가질 수 있습니다.
  • ViewModel은 다른 데이터 모델 또는 데이터 소스의 여러 엔티티 또는 객체를 가질 수 있습니다.

뷰 모델 설계

public class UserLoginViewModel 
{ 
[Required(ErrorMessage = "Please enter your username")] 
[Display(Name = "User Name")]
[MaxLength(50)]
public string UserName { get; set; }
 [Required(ErrorMessage = "Please enter your password")]
 [Display(Name = "Password")]
 [MaxLength(50)]
 public string Password { get; set; } 
} 

뷰에서 뷰 모델 제시

@model MyModels.UserLoginViewModel 
@{
 ViewBag.Title = "User Login";
 Layout = "~/Views/Shared/_Layout.cshtml";
}
@using (Html.BeginForm())
{
<div class="editor-label">
 @Html.LabelFor(m => m.UserName)
</div>
<div class="editor-field">
 @Html.TextBoxFor(m => m.UserName)
 @Html.ValidationMessageFor(m => m.UserName)
</div>
<div class="editor-label">
 @Html.LabelFor(m => m.Password)
</div>
<div class="editor-field">
 @Html.PasswordFor(m => m.Password)
 @Html.ValidationMessageFor(m => m.Password)
</div>
<p>
 <input type="submit" value="Log In" />
</p>
</div>
}

액션으로 작업하기

public ActionResult Login()
{ 
return View();
}
[HttpPost]
public ActionResult Login(UserLoginViewModel user)
{
// To acces data using LINQ
DataClassesDataContext mobjentity = new DataClassesDataContext();
 if (ModelState.IsValid) 
{ 
try
 {
 var q = mobjentity.tblUsers.Where(m => m.UserName == user.UserName && m.Password == user.Password).ToList(); 
 if (q.Count > 0) 
 { 
 return RedirectToAction("MyAccount");
 }
 else
 {
 ModelState.AddModelError("", "The user name or password provided is incorrect.");
 }
 }
 catch (Exception ex)
 {
 } 
 } 
 return View(user);
} 
  1. ViewModel에는보기 / 페이지에 표시하려는 필드 / 데이터 만 넣으십시오.
  2. 뷰는 ViewModel의 속성을 반영하기 때문에 렌더링 및 유지 관리가 쉽습니다.
  3. ViewModel이 더 복잡해지면 매퍼를 사용하십시오.

1

뷰 모델은 뷰에서 데이터를 렌더링하는 데 사용할 수있는 클래스입니다. 두 개의 엔티티 Place 및 PlaceCategory가 있고 단일 모델을 사용하여 두 엔티티의 데이터에 액세스하려는 경우 ViewModel을 사용한다고 가정하십시오.

  public class Place
    {
       public int PlaceId { get; set; }
        public string PlaceName { get; set; }
        public string Latitude { get; set; }
        public string Longitude { get; set; }
        public string BestTime { get; set; }
    }
    public class Category
    {
        public int ID { get; set; }
        public int? PlaceId { get; set; }
        public string PlaceCategoryName { get; set; }
        public string PlaceCategoryType { get; set; }
    }
    public class PlaceCategoryviewModel
    {
        public string PlaceName { get; set; }
        public string BestTime { get; set; }
        public string PlaceCategoryName { get; set; }
        public string PlaceCategoryType { get; set; }
    }

위의 예에서 장소 및 카테고리는 서로 다른 두 엔티티이며 PlaceCategory보기 모델은 View에서 사용할 수있는 ViewModel입니다.


귀하의 예는 명확하지 않습니다. 위에서 언급 한 것은 ViewModel이 데이터를 해당 뷰에 연결한다는 것입니다. BlipAjax의 ViewModels를 보면 완벽하게 맞는 클래스를 볼 수 있습니다.
허먼 반 데르 Blom

0

ViewModels를 사용하여 "기준"웹 응용 프로그램을 설정하는 방법을 코드로 연구하려면 GitHub에서 https://github.com/ajsaulsberry/BlipAjax 코드를 다운로드하는 것이 좋습니다. 대기업 응용 프로그램을 개발했습니다. 이렇게하면이 "ViewModel"기능을 모두 처리하는 훌륭한 아키텍처를 설정하는 것이 문제가됩니다. BlipAjax를 사용하면 아주 좋은 "기준선"을 갖게 될 것입니다. 단순한 웹 사이트이지만 단순성이 뛰어납니다. 나는 그들이 응용 프로그램에서 실제로 필요한 것을 지적하기 위해 영어를 사용한 방식을 좋아합니다.

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