What is ViewModel in MVC?

I am new to ASP.NET MVC . I have a problem in understanding the purpose of a ViewModel .

What is a ViewModel and why do we need a ViewModel for an ASP.NET MVC application?

It is better if I can have a simple example.


A view model represents the data that you want to display on your view/page, whether it be used for static text or for input values (like textboxes and dropdown lists) that can be added to the database (or edited). It is something different than your domain model . It is a model for the view.

Let us say that you have an Employee class that represents your employee domain model and it contains the following properties (unique identifier, first name, last name and date created):

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

     public string FirstName { get; set; }

     public string LastName { get; set; }

     public DateTime DateCreated { get; set; }
}

View models differ from domain models in that view models only contain the data (represented by properties) that you want to use on your view. For example, lets say that you want to add a new employee record, your view model might look like this:

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

     public string LastName { get; set; }
}

As you can see it only contains two of the properties. These two properties are also in the employee domain model. Why is this you may ask? Id might not be set from the view, it might be auto generated by the Employee table. And DateCreated might also be set in the stored procedure or in the service layer of your application. So Id and DateCreated are not needed in the view model. You might want to display these two properties when you view an employee's details (an employee that has already been captured) as static text.

When loading the view/page, the create action method in your employee controller will create an instance of this view model, populate any fields if required, and then pass this view model to the view/page:

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
     }
}

Your view/page might look like this (assuming you are using ASP.NET MVC and the Razor view engine):

@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>

Validation would thus be done only on FirstName and LastName . Using Fluent Validation you might have validation like this:

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");
     }
}

And with Data Annotations it might look this:

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; }
}

The key thing to remember is that the view model only represents the data that you want to use , nothing else. You can imagine all the unnecessary code and validation if you have a domain model with 30 properties and you only want to update a single value. Given this scenario you would only have this one value/property in the view model and not all the properties that are in the domain object.

A view model might not only have data from one database table. It can combine data from another table. Take my example above about adding a new employee record. Besides adding just the first and last names you might also want to add the department of the employee. This list of departments will come from your Departments table. So now you have data from the Employees and Departments tables in one view model. You will just then need to add the following two properties to your view model and populate it with data:

public int DepartmentId { get; set; }

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

When editing employee data (an employee that has already been added to the database) it wouldn't differ much from my example above. Create a view model, call it for example EditEmployeeViewModel . Only have the data that you want to edit in this view model, like first name and last name. Edit the data and click the submit button. I wouldn't worry too much about the Id field because the Id value will probably been in the URL, for example:

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

Take this Id and pass it through to your repository layer, together with your first name and last name values.

When deleting a record, I normally follow the same path as with the edit view model. I would also have a URL, for example:

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

When the view loads up for the first time I would get the employee's data from the database using the Id of 3. I would then just display static text on my view/page so that the user can see what employee is being deleted. When the user clicks the Delete button, I would just use the Id value of 3 and pass it to my repository layer. You only need the Id to delete a record from the table.

Another point, you don't really need a view model for every action. If it is simple data then it would be fine to only use EmployeeViewModel . If it is complex views/pages and they differ from each other then I would suggest you use separate view models for each.

I hope this clears up any confusion that you had about view models and domain models.


View model is a class that represents the data model used in a specific view. We could use this class as a model for a login page:

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; }
}

Using this view model you can define the view (Razor view engine):

@model CamelTrap.Models.ViewModels.LoginPageVM

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

And actions:

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

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

Which produces this result (screen is taken after submitting form, with validation messages):

As you can see, a view model has many roles:

  • View models documents a view by consisting only fields, that are represented in view.
  • View models may contain specific validation rules using data annotations or IDataErrorInfo.
  • View model defines how a view should look (for LabelFor , EditorFor , DisplayFor helpers).
  • View models can combine values from different database entities.
  • You can specify easily display templates for view models and reuse them in many places using DisplayFor or EditorFor helpers.
  • Another example of a view model and its retrieval: We want to display basic user data, his privileges and users name. We create a special view model, which contains only the required fields. We retrieve data from different entities from database, but the view is only aware of the view model class:

    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; }
    }
    

    Retrieval:

    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
    } 
    

    Edit: I updated this answer on my Blog:

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

    My answer is a bit lengthy but I think it is important to compare view models to other types of commonly used models to understand why they are different and why they are necessary.

    To summarize, and to directly answer the question that is asked:

    Generally speaking, a view model is an object that contains all the properties and methods necessary to render a view. View model properties are often related to data objects such as customers and orders and in addition they also contain properties related to the page or application itself such as user name, application name etc. View models provide a convenient object to pass to a rendering engine to create a html page. One of many reasons to use a view model is that view models provide a way to unit test certain presentation tasks such as handling user input, validating data, retrieving data for display, etc.

    Here is a comparison of Entity models (a.ka. DTO's a.ka. models), Presentation Models, and View Models.

    Data Transfer Objects aka “Model”

    A Data Transfer Object (DTO) is a class with properties that match a table schema in a database. DTO's are named for their common usage for shuttling data to and from a data store.
    Characteristics of DTO's:

    • Are business objects – their definition is dependent on application data.

    • Usually contain properties only – no code.

    • Primarily used for transporting data to and from a database.

    • Properties exactly or closely match fields on a specific table in a data store.

    Database tables are usually normalized therefore DTO's are usually normalized also. This makes them of limited use for presenting data. However, for certain simple data structures they often do quite well.

    Here are two examples of what DTO's might look like:

    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; }
    }
    

    Presentation Models

    A presentation model is a utility class that is used to render data on a screen or report. Presentation models are typically used to model complex data structures that are composed from data from multiple DTO's. Presentation models often represent a denormalized view of data.

    Characteristics of Presentation Models:

    • Are business objects – their definition is dependent on application data.

    • Contain mostly properties. Code is typically limited to formatting data or converting to or from a DTO. Presentation Models should not contain business logic.

    • Often present a denormalized view of data. That is, they often combine properties from multiple DTO's.

    • Often contain properties of a different base type than a DTO. For example dollar amounts may be represented as strings so they can contain commas and a currency symbol.

    • Often defined by how they are used as well as their object characteristics. In other words, a simple DTO that is used as the backing model for rendering a grid is in fact also a presentation model in the context of that grid.

    Presentation models are used “as needed” and “where needed” (whereas DTO's are usually tied to the database schema). A presentation model may be used to model data for an entire page, a grid on a page, or a dropdown on a grid on a page. Presentation models often contain properties that are other presentation models. Presentation models are often constructed for a single-use purpose such as to render a specific grid on a single page.

    An example presentation model:

    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); } }
    }
    

    View Models

    A view model is similar to a presentation model in that is a backing class for rendering a view. However it is very different from a Presentation Model or a DTO in how it is constructed. View models often contain the same properties as presentation models and DTO's and for this reason they are often confused one for the other.

    Characteristics of View Models:

    • Are the single source of data used to render a page or screen. Usually this means that a view model will expose every property that any control on the page will need to render itself correctly. Making the view model the single source of data for the view greatly improves its capability and value for unit testing.

    • Are composite objects that contain properties that consist of application data as well as properties that are used by application code. This characteristic is crucial when designing the view model for reusability and is discussed in the examples below.

    • Contain application code. View Models usually contain methods that are called during rendering and when the user is interacting with the page. This code typically relates to event handling, animation, visibility of controls, styling, etc.

    • Contain code that calls business services for the purpose of retrieving data or sending it to a database server. This code is often mistakenly placed in a controller. Calling business services from a controller usually limits the usefulness of the view model for unit testing. To be clear, view models themselves should not contain business logic but should make calls to services which do contain business logic.

    • Often contain properties which are other view models for other pages or screens.

    • Are written “per page” or “per screen”. A unique View Model is typically written for every page or screen in an application.

    • Usually derive from a base class since most pages and screens share common properties.

    View Model Composition

    As stated earlier, view models are composite objects in that they combine application properties and business data properties on a single object. Examples of commonly used application properties that are used on view models are:

    • Properties that are used to display application state such as error messages, user name, status, etc.

    • Properties used to format, display, stylize, or animate controls.

    • Properties used for data binding such as list objects and properties that hold intermediate data that is input by the user.

    The following examples show why the composite nature of view models is important and how we can best construct a View Model that efficient and reusable.

    Assume we are writing a web application. One of the requirements of the application design is that the page title, user name, and application name must be displayed on every page. If we want to create a page to display a presentation order object, we may modify the presentation model as follows:

    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); } }
    }
    

    This design might work… but what if we want to create a page that will display a list of orders? The PageTitle, UserName, and ApplicationName properties will be repeated and become unwieldy to work with. Also, what if we want to define some page-level logic in the constructor of the class? We can no longer do that if we create an instance for every order that will be displayed.

    Composition over inheritance

    Here is a way we might re-factor the order presentation model such that it become a true view model and will be useful for displaying a single PresentationOrder object or a collection of PresentationOrder objects:

    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; }
    }
    

    Looking at the above two classes we can see that one way to think about a view model is that it is a presentation model that contains another presentation model as a property. The top level presentation model (ie view model) contains properties that are relevant to the page or application while presentation model (property) contains properties that are relevant to application data.

    We can take our design a step further and create a base view model class that can be used not only for PresentationOrders, but for any other class as well:

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

    Now we can simplify our PresentationOrderVM like this:

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

    We can make our BaseViewModel even more re-usable by making it generic:

    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; }
    }
    

    Now our implementations are effortless:

    public class PresentationOrderVM : BaseViewModel<PresentationOrder>
    {
        // done!
    }
    
    public class PresentationOrderVM : BaseViewModel<List<PresentationOrder>>
    {
        // done!
    }
    
    链接地址: http://www.djcxy.com/p/82108.html

    上一篇: PHP中的接口是什么?

    下一篇: 什么是MVC中的ViewModel?