ViewModel trong MVC là gì?


429

Tôi chưa quen với ASP.NET MVC. Tôi có một vấn đề với việc hiểu mục đích của ViewModel.

ViewModel là gì và tại sao chúng ta cần ViewModel cho ứng dụng ASP.NET MVC?

Nếu tôi có được một ví dụ tốt về hoạt động và giải thích của nó sẽ tốt hơn.


4
Bài đăng này là những gì bạn tìm kiếm - "ASP.NET MVC ViewModel là gì?"
Yusubov

6
Bài viết này có vẻ hay: rachelappel.com/ Hãy
Andrew

có thể trùng lặp trong MVC, ViewModel là gì?
rogerdeuce

Câu trả lời:


607

A view modelđại diện cho dữ liệu bạn muốn hiển thị trên chế độ xem / trang của bạn, cho dù nó được sử dụng cho văn bản tĩnh hoặc cho các giá trị đầu vào (như hộp văn bản và danh sách thả xuống) có thể được thêm vào cơ sở dữ liệu (hoặc chỉnh sửa). Nó là một cái gì đó khác với của bạn domain model. Nó là một mô hình cho quan điểm.

Giả sử bạn có một Employeelớp đại diện cho mô hình miền nhân viên của bạn và nó chứa các thuộc tính sau (mã định danh duy nhất, tên, họ và ngày được tạo):

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

     public string FirstName { get; set; }

     public string LastName { get; set; }

     public DateTime DateCreated { get; set; }
}

Các mô hình xem khác với các mô hình miền trong các mô hình xem đó chỉ chứa dữ liệu (được biểu thị bằng các thuộc tính) mà bạn muốn sử dụng trên chế độ xem của mình. Ví dụ: giả sử bạn muốn thêm một bản ghi nhân viên mới, mô hình xem của bạn có thể trông như thế này:

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

     public string LastName { get; set; }
}

Như bạn có thể thấy nó chỉ chứa hai trong số các thuộc tính. Hai thuộc tính này cũng nằm trong mô hình miền nhân viên. Tại sao điều này bạn có thể hỏi? Idcó thể không được đặt từ chế độ xem, nó có thể được tạo tự động bởi bảng Nhân viên. Và DateCreatedcũng có thể được đặt trong thủ tục được lưu trữ hoặc trong lớp dịch vụ của ứng dụng của bạn. Vì vậy IdDateCreatedkhông cần thiết trong mô hình xem. Bạn có thể muốn hiển thị hai thuộc tính này khi bạn xem chi tiết của nhân viên (một nhân viên đã bị bắt) dưới dạng văn bản tĩnh.

Khi tải chế độ xem / trang, phương thức tạo hành động trong bộ điều khiển nhân viên của bạn sẽ tạo một phiên bản của mô hình chế độ xem này, điền vào bất kỳ trường nào nếu được yêu cầu và sau đó chuyển mô hình xem này cho chế độ xem / trang:

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

Chế độ xem / trang của bạn có thể trông như thế này (giả sử bạn đang sử dụng ASP.NET MVCRazorcông cụ xem):

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

Xác nhận do đó sẽ chỉ được thực hiện trên FirstNameLastName. Sử dụng FluentValidation bạn có thể có xác nhận như thế này:

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

Và với Chú thích dữ liệu, nó có thể trông như thế này:

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

Điều quan trọng cần nhớ là mô hình khung nhìn chỉ đại diện cho dữ liệu mà bạn muốn sử dụng , không có gì khác. Bạn có thể tưởng tượng tất cả các mã và xác nhận không cần thiết nếu bạn có một mô hình miền với 30 thuộc tính và bạn chỉ muốn cập nhật một giá trị duy nhất. Với kịch bản này, bạn sẽ chỉ có một giá trị / thuộc tính này trong mô hình khung nhìn và không phải tất cả các thuộc tính có trong đối tượng miền.

Một mô hình xem có thể không chỉ có dữ liệu từ một bảng cơ sở dữ liệu. Nó có thể kết hợp dữ liệu từ một bảng khác. Lấy ví dụ của tôi ở trên về việc thêm một hồ sơ nhân viên mới. Bên cạnh việc chỉ thêm tên và họ, bạn cũng có thể muốn thêm bộ phận của nhân viên. Danh sách các phòng ban sẽ đến từ Departmentsbảng của bạn . Vì vậy, bây giờ bạn có dữ liệu từ EmployeesDepartmentsbảng trong một mô hình xem. Sau đó, bạn sẽ cần thêm hai thuộc tính sau vào mô hình xem của mình và điền dữ liệu vào đó:

public int DepartmentId { get; set; }

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

Khi chỉnh sửa dữ liệu nhân viên (một nhân viên đã được thêm vào cơ sở dữ liệu), nó sẽ không khác nhiều so với ví dụ của tôi ở trên. Tạo một mô hình xem, gọi nó là ví dụ EditEmployeeViewModel. Chỉ có dữ liệu mà bạn muốn chỉnh sửa trong mô hình chế độ xem này, như tên và họ. Chỉnh sửa dữ liệu và nhấp vào nút gửi. Tôi sẽ không lo lắng quá nhiều về Idlĩnh vực này vì Idgiá trị có thể sẽ nằm trong URL, ví dụ:

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

Lấy cái này Idvà chuyển nó qua lớp kho lưu trữ của bạn, cùng với các giá trị tên và họ của bạn.

Khi xóa một bản ghi, tôi thường đi theo cùng một đường dẫn như với mô hình xem chỉnh sửa. Tôi cũng sẽ có một URL, ví dụ:

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

Khi chế độ xem tải lên lần đầu tiên, tôi sẽ lấy dữ liệu của nhân viên từ cơ sở dữ liệu bằng cách sử dụng Idsố 3. Tôi sẽ chỉ hiển thị văn bản tĩnh trên chế độ xem / trang của mình để người dùng có thể thấy nhân viên nào đang bị xóa. Khi người dùng nhấp vào nút Xoá, tôi sẽ chỉ sử dụng Idgiá trị 3 và chuyển nó vào lớp kho lưu trữ của mình. Bạn chỉ cần Idxóa một bản ghi từ bảng.

Một điểm khác, bạn không thực sự cần một mô hình xem cho mọi hành động. Nếu đó là dữ liệu đơn giản thì sẽ tốt thôi nếu chỉ sử dụng EmployeeViewModel. Nếu đó là các chế độ xem / trang phức tạp và chúng khác nhau thì tôi sẽ đề nghị bạn sử dụng các mô hình xem riêng biệt cho từng trang.

Tôi hy vọng điều này sẽ làm sáng tỏ mọi sự nhầm lẫn mà bạn có về các mô hình xem và mô hình miền.


5
@Kenny: Sau đó hiển thị nó :) Những gì tôi đã cố gắng nói là hãy nói rằng bạn có một mô hình miền với 50 thuộc tính và chế độ xem của bạn chỉ cần hiển thị 5 thì không thể gửi tất cả 50 thuộc tính chỉ để hiển thị 5.
Brendan Vogt

5
@BrendanVogt - bạn đã làm rất tốt khi giải thích điều đó, nhưng tôi không hiểu chi phí của việc "gửi tất cả 50 tài sản" là bao nhiêu. Các mã khác đã tạo một đối tượng Model, với tất cả 50 thuộc tính và dường như không đáng để duy trì một lớp khác chỉ để không gửi 45 thuộc tính - đặc biệt nếu bạn có thể muốn gửi bất kỳ một trong số 45 thuộc tính đó trong tương lai.
Kenny Evitt

5
@BrendanVogt - Tôi nghĩ có lẽ câu trả lời của LukLed giúp tôi hiểu lý do tại sao những điều này có thể hữu ích, đặc biệt là ViewModel (có thể) "... kết hợp các giá trị từ các thực thể cơ sở dữ liệu khác nhau" [trong đó tôi cho rằng cụm từ này đúng như vậy " thực thể cơ sở dữ liệu "được thay thế bằng" Đối tượng mô hình "]. Tuy nhiên, ViewModels có ý định giải quyết vấn đề cụ thể nào? bạn có đường liên kết nào không? Tôi không thể tìm thấy bất cứ điều gì bản thân mình. [Và tôi xin lỗi nếu tôi dường như đang chọn bạn!]
Kenny Evitt 17/07/13

1
Tôi chỉ nghe ai đó nói rằng ViewModels là một cách tốt để gửi nhiều bộ sưu tập (hoặc thuộc tính mô hình chéo) vào một chế độ xem mà không phải nhét chúng vào viewBag. Có nghĩa với tôi.
Ayyash

3
Tôi xin lỗi vì đã chỉ trích nhưng thật không may, câu trả lời này không đầy đủ. Xác định chế độ xem chỉ là những gì bạn cần hiển thị trên trang của mình giống như hỏi "Xe hơi là gì?" và nhận được câu trả lời "Nó không phải là máy bay". Vâng đó là sự thật nhưng không hữu ích lắm. Định nghĩa chính xác hơn về VM là "Mọi thứ bạn cần để hiển thị trang của bạn." Nếu bạn đọc xuống phía dưới, tôi đã xác định được các thành phần bạn cần để xây dựng VM của bạn một cách chính xác và dễ dàng, trong nhiều trường hợp tận dụng các mô hình miền và mô hình trình bày hiện tại của bạn.
Sam

133

Mô hình khung nhìn là một lớp biểu thị mô hình dữ liệu được sử dụng trong một khung nhìn cụ thể. Chúng ta có thể sử dụng lớp này làm mô hình cho trang đăng nhập:

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

Sử dụng mô hình khung nhìn này, bạn có thể xác định chế độ xem (Công cụ xem dao cạo):

@model CamelTrap.Models.ViewModels.LoginPageVM

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

Và hành động:

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

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

Cái nào tạo ra kết quả này (màn hình được lấy sau khi gửi biểu mẫu, với thông báo xác thực):

Như bạn có thể thấy, một mô hình khung nhìn có nhiều vai trò:

  • Xem mô hình tài liệu một khung nhìn bằng cách chỉ bao gồm các trường, được thể hiện trong khung nhìn.
  • Các mô hình xem có thể chứa các quy tắc xác thực cụ thể bằng cách sử dụng chú thích dữ liệu hoặc IDataErrorInfo.
  • Xem mô hình định nghĩa như thế nào một cái nhìn nên nhìn (ví LabelFor, EditorFor, DisplayFornhững người giúp đỡ).
  • Xem mô hình có thể kết hợp các giá trị từ các thực thể cơ sở dữ liệu khác nhau.
  • Bạn có thể chỉ định các mẫu hiển thị dễ dàng cho các mô hình xem và sử dụng lại chúng ở nhiều nơi bằng trình trợ giúp DisplayFor hoặc EditorFor.

Một ví dụ khác về mô hình khung nhìn và truy xuất mô hình: Chúng tôi muốn hiển thị dữ liệu người dùng cơ bản, đặc quyền và tên người dùng của anh ấy. Chúng tôi tạo một mô hình khung nhìn đặc biệt, chỉ chứa các trường bắt buộc. Chúng tôi lấy dữ liệu từ các thực thể khác nhau từ cơ sở dữ liệu, nhưng chế độ xem chỉ biết về lớp mô hình xem:

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

Truy tìm:

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
} 

Tôi làm mỏng người dùng.Mother.FirstName + "" + user.Mother.LastName nên được thực hiện trong View Model End. Tất cả Logic phải được thực hiện ở phần cuối Mô hình Xem.
Kurkula

3
@Chandana: Tôi tin rằng việc ghép đơn giản có thể được thực hiện trong mô hình xem. Không có lý do để phơi bày hai lĩnh vực, nếu chúng có nghĩa là được trình bày cùng nhau.
LukLed

82

Chỉnh sửa: Tôi đã cập nhật câu trả lời này trên Blog của mình:

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

Câu trả lời của tôi hơi dài dòng nhưng tôi nghĩ điều quan trọng là so sánh các mô hình xem với các loại mô hình thường được sử dụng khác để hiểu tại sao chúng khác nhau và tại sao chúng lại cần thiết.

Để tóm tắt và trả lời trực tiếp câu hỏi được hỏi:

Nói chung, mô hình khung nhìn là một đối tượng chứa tất cả các thuộc tính và phương thức cần thiết để hiển thị một khung nhìn. Các thuộc tính mô hình xem thường liên quan đến các đối tượng dữ liệu như khách hàng và đơn đặt hàng và ngoài ra chúng cũng chứa các thuộc tính liên quan đến trang hoặc chính ứng dụng như tên người dùng, tên ứng dụng, v.v. Các mô hình xem cung cấp một đối tượng thuận tiện để chuyển đến một công cụ kết xuất tạo một trang html. Một trong nhiều lý do để sử dụng mô hình khung nhìn là các mô hình khung nhìn cung cấp một cách để kiểm tra đơn vị một số tác vụ trình bày nhất định như xử lý dữ liệu nhập của người dùng, xác thực dữ liệu, truy xuất dữ liệu để hiển thị, v.v.

Dưới đây là so sánh các mô hình Thực thể (mô hình a.ka. a.ka. của DTO), Mô hình trình bày và Mô hình xem.

Đối tượng truyền dữ liệu hay còn gọi là Model Model

Đối tượng truyền dữ liệu (DTO) là một lớp có các thuộc tính khớp với lược đồ bảng trong cơ sở dữ liệu. DTO được đặt tên theo cách sử dụng phổ biến của họ để chuyển dữ liệu đến và từ một cửa hàng dữ liệu.
Đặc điểm của DTO:

• Là đối tượng kinh doanh - định nghĩa của chúng phụ thuộc vào dữ liệu ứng dụng.

• Thường chỉ chứa các thuộc tính - không có mã.

• Chủ yếu được sử dụng để vận chuyển dữ liệu đến và từ cơ sở dữ liệu.

• Thuộc tính chính xác hoặc khớp các trường trên một bảng cụ thể trong kho lưu trữ dữ liệu.

Các bảng cơ sở dữ liệu thường được chuẩn hóa do đó các DTO thường cũng được chuẩn hóa. Điều này làm cho chúng được sử dụng hạn chế để trình bày dữ liệu. Tuy nhiên, đối với các cấu trúc dữ liệu đơn giản nhất định, chúng thường làm khá tốt.

Đây là hai ví dụ về hình dạng của 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; }
}

Mô hình trình bày

Mô hình trình bày là một lớp tiện ích được sử dụng để hiển thị dữ liệu trên màn hình hoặc báo cáo. Các mô hình trình bày thường được sử dụng để mô hình các cấu trúc dữ liệu phức tạp được tạo từ dữ liệu từ nhiều DTO. Các mô hình trình bày thường đại diện cho một cái nhìn không chuẩn hóa dữ liệu.

Đặc điểm của mô hình trình bày:

• Là đối tượng kinh doanh - định nghĩa của chúng phụ thuộc vào dữ liệu ứng dụng.

• Chứa hầu hết các thuộc tính. Mã thường được giới hạn trong việc định dạng dữ liệu hoặc chuyển đổi sang hoặc từ DTO. Mô hình trình bày không nên chứa logic kinh doanh.

• Thường trình bày một cái nhìn không chuẩn hóa dữ liệu. Đó là, họ thường kết hợp các thuộc tính từ nhiều DTO.

• Thường chứa các thuộc tính của loại cơ sở khác với DTO. Ví dụ, số tiền có thể được biểu diễn dưới dạng chuỗi để chúng có thể chứa dấu phẩy và ký hiệu tiền tệ.

• Thường được xác định bởi cách chúng được sử dụng cũng như các đặc điểm đối tượng của chúng. Nói cách khác, một DTO đơn giản được sử dụng làm mô hình sao lưu để hiển thị lưới trên thực tế cũng là một mô hình trình bày trong ngữ cảnh của lưới đó.

Các mô hình trình bày được sử dụng, khi cần thiết và các nơi cần thiết (trong khi DTO thường được gắn với lược đồ cơ sở dữ liệu). Một mô hình trình bày có thể được sử dụng để mô hình hóa dữ liệu cho toàn bộ trang, lưới trên một trang hoặc thả xuống trên lưới trên một trang. Các mô hình trình bày thường chứa các thuộc tính là các mô hình trình bày khác. Các mô hình trình bày thường được xây dựng cho một mục đích sử dụng duy nhất như để hiển thị một lưới cụ thể trên một trang.

Một mô hình trình bày ví dụ:

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

Xem mô hình

Mô hình khung nhìn tương tự như mô hình trình bày trong đó là lớp hỗ trợ để hiển thị chế độ xem. Tuy nhiên, nó rất khác với Mô hình trình bày hoặc DTO về cách thức xây dựng nó. Các mô hình xem thường chứa các thuộc tính giống như các mô hình trình bày và của DTO và vì lý do này, chúng thường bị nhầm lẫn với nhau.

Đặc điểm của mô hình xem:

• Là nguồn dữ liệu duy nhất được sử dụng để hiển thị trang hoặc màn hình. Thông thường, điều này có nghĩa là một mô hình chế độ xem sẽ hiển thị mọi thuộc tính mà mọi điều khiển trên trang sẽ cần hiển thị chính xác. Làm cho mô hình khung nhìn trở thành nguồn dữ liệu duy nhất cho khung nhìn giúp cải thiện đáng kể khả năng và giá trị của nó cho thử nghiệm đơn vị.

• Là các đối tượng tổng hợp có chứa các thuộc tính bao gồm dữ liệu ứng dụng cũng như các thuộc tính được sử dụng bởi mã ứng dụng. Đặc tính này rất quan trọng khi thiết kế mô hình khung nhìn cho khả năng sử dụng lại và được thảo luận trong các ví dụ dưới đây.

• Chứa mã ứng dụng. Mô hình xem thường chứa các phương thức được gọi trong khi kết xuất và khi người dùng tương tác với trang. Mã này thường liên quan đến xử lý sự kiện, hoạt hình, khả năng hiển thị của các điều khiển, kiểu dáng, v.v.

• Chứa mã gọi các dịch vụ kinh doanh cho mục đích lấy dữ liệu hoặc gửi nó đến một máy chủ cơ sở dữ liệu. Mã này thường bị đặt nhầm trong bộ điều khiển. Gọi các dịch vụ kinh doanh từ bộ điều khiển thường giới hạn tính hữu ích của mô hình xem để thử nghiệm đơn vị. Để rõ ràng, bản thân các mô hình xem không nên chứa logic nghiệp vụ mà nên thực hiện các cuộc gọi đến các dịch vụ có chứa logic nghiệp vụ.

• Thường chứa các thuộc tính là các mô hình xem khác cho các trang hoặc màn hình khác.

• Được viết trên mỗi trang Tử hoặc trên mỗi màn hình. Mô hình Chế độ xem duy nhất thường được viết cho mọi trang hoặc màn hình trong một ứng dụng.

• Thường xuất phát từ một lớp cơ sở vì hầu hết các trang và màn hình chia sẻ các thuộc tính chung.

Xem thành phần mô hình

Như đã nêu trước đó, các mô hình xem là các đối tượng tổng hợp trong đó chúng kết hợp các thuộc tính ứng dụng và thuộc tính dữ liệu nghiệp vụ trên một đối tượng. Ví dụ về các thuộc tính ứng dụng thường được sử dụng trên các mô hình xem là:

• Các thuộc tính được sử dụng để hiển thị trạng thái ứng dụng như thông báo lỗi, tên người dùng, trạng thái, v.v.

• Thuộc tính được sử dụng để định dạng, hiển thị, cách điệu hoặc điều khiển hoạt hình.

• Thuộc tính được sử dụng để liên kết dữ liệu, chẳng hạn như liệt kê các đối tượng và thuộc tính chứa dữ liệu trung gian được người dùng nhập vào.

Các ví dụ sau đây cho thấy lý do tại sao tính chất tổng hợp của các mô hình xem là quan trọng và làm thế nào chúng ta có thể xây dựng Mô hình Chế độ xem hiệu quả và có thể tái sử dụng tốt nhất.

Giả sử chúng ta đang viết một ứng dụng web. Một trong những yêu cầu của thiết kế ứng dụng là tiêu đề trang, tên người dùng và tên ứng dụng phải được hiển thị trên mỗi trang. Nếu chúng tôi muốn tạo một trang để hiển thị một đối tượng thứ tự trình bày, chúng tôi có thể sửa đổi mô hình trình bày như sau:

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

Thiết kế này có thể hoạt động được nhưng nếu chúng ta muốn tạo một trang sẽ hiển thị danh sách các đơn đặt hàng thì sao? Các thuộc tính PageTitle, UserName và ApplicationName sẽ được lặp lại và trở nên khó sử dụng. Ngoài ra, nếu chúng ta muốn định nghĩa một số logic cấp độ trang trong hàm tạo của lớp thì sao? Chúng tôi không còn có thể làm điều đó nếu chúng tôi tạo một phiên bản cho mỗi đơn hàng sẽ được hiển thị.

Thành phần trên thừa kế

Dưới đây là cách chúng ta có thể xác định lại mô hình trình bày thứ tự để nó trở thành mô hình khung nhìn thực và sẽ hữu ích để hiển thị một đối tượng PresentationOrder hoặc một bộ sưu tập các đối tượng 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; }
}

Nhìn vào hai lớp ở trên, chúng ta có thể thấy rằng một cách để suy nghĩ về một mô hình khung nhìn là nó là một mô hình trình bày có chứa một mô hình trình bày khác như một thuộc tính. Mô hình trình bày cấp cao nhất (ví dụ mô hình xem) chứa các thuộc tính có liên quan đến trang hoặc ứng dụng trong khi mô hình trình bày (thuộc tính) chứa các thuộc tính có liên quan đến dữ liệu ứng dụng.

Chúng ta có thể tiến lên một bước nữa và tạo ra một lớp mô hình khung nhìn cơ sở có thể được sử dụng không chỉ cho PresentationOrder, mà còn cho bất kỳ lớp nào khác:

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

Bây giờ chúng ta có thể đơn giản hóa PresentationOrderVM của mình như thế này:

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

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

Chúng tôi có thể làm cho BaseViewModel của chúng tôi có thể sử dụng lại nhiều hơn bằng cách đặt nó chung chung:

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

Bây giờ việc thực hiện của chúng tôi là dễ dàng:

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

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

2
Sam cảm ơn bạn !! điều này giúp tôi hoàn toàn nắm bắt được thực thể đa diện đó là: Mô hình xem. Tôi là một sinh viên đại học chỉ học kiến ​​trúc MVC và điều này đã làm rõ một loạt các chức năng có khả năng được tiếp xúc với nhà phát triển. Nếu tôi có thể tôi sẽ đặt một ngôi sao bên cạnh câu trả lời của bạn.
Chef_Code

1
@Sam 'Các mô hình xem thường chứa các thuộc tính giống như các mô hình trình bày và các mô hình DTO và vì lý do này, chúng thường bị nhầm lẫn với nhau.' Điều đó có nghĩa là chúng thường được sử dụng thay cho các mô hình trình bày hay chúng có nghĩa là chứa các mô hình trình bày / dtos?
Alexander Derck

2
@AlexanderDerck Chúng được sử dụng cho các mục đích khác nhau. Họ đang nhầm lẫn cái này cho cái kia (lỗi). Không, thông thường bạn sẽ không sử dụng mô hình đặt trước thay cho mô hình xem. Phổ biến hơn nhiều là VM "chứa" mô hình trình bày tức là MyViewModel<MyPresModel>
Sam

2
@Sam Giả sử các đối tượng mô hình là các đối tượng trực tiếp, ví dụ như các mô hình nhibernate .. vậy bằng cách có BusinessObject, chúng ta không phơi bày trực tiếp mô hình / đối tượng sống cho chế độ xem? tức là đối tượng kinh doanh có thể được sử dụng để sửa đổi trạng thái cơ sở dữ liệu trực tiếp? Ngoài ra, những gì về mô hình xem lồng nhau? Điều đó sẽ đòi hỏi nhiều thuộc tính đối tượng kinh doanh, phải không?
Muhammad Ali

22

Nếu bạn có các thuộc tính cụ thể cho chế độ xem và không liên quan đến kho lưu trữ DB / Dịch vụ / Dữ liệu, thì nên sử dụng ViewModels. Giả sử, bạn muốn để lại một hộp kiểm được chọn dựa trên trường DB (hoặc hai) nhưng bản thân trường DB không phải là boolean. Mặc dù có thể tạo các thuộc tính này trong Mô hình và giữ nó ẩn khỏi ràng buộc với dữ liệu, bạn có thể không muốn làm lộn xộn Mô hình tùy thuộc vào số lượng các trường và giao dịch đó.

Nếu có quá ít dữ liệu và / hoặc biến đổi chế độ xem cụ thể, bạn có thể sử dụng chính Mô hình


19

Tôi đã không đọc tất cả các bài viết nhưng mọi câu trả lời dường như thiếu một khái niệm thực sự giúp tôi "hiểu" ...

Nếu một Mô hình gần giống với Bảng cơ sở dữ liệu , thì ViewModel gần giống với Cơ sở dữ liệu Xem - Một khung nhìn thường trả về một lượng nhỏ dữ liệu từ một bảng hoặc, các bộ dữ liệu phức tạp từ nhiều bảng (tham gia).

Tôi thấy mình đang sử dụng ViewModels để chuyển thông tin vào dạng xem / biểu mẫu và sau đó chuyển dữ liệu đó thành Mô hình hợp lệ khi biểu mẫu gửi lại cho bộ điều khiển - cũng rất tiện để lưu trữ Danh sách (IEnumerable).


11

MVC không có viewmodel: nó có mô hình, khung nhìn và bộ điều khiển. Một viewmodel là một phần của MVVM (Model-View-Viewmodel). MVVM có nguồn gốc từ Mô hình trình bày và được phổ biến trong WPF. Cũng cần có một mô hình trong MVVM, nhưng hầu hết mọi người đều bỏ lỡ hoàn toàn điểm của mẫu đó và họ sẽ chỉ có chế độ xem và chế độ xem. Mô hình trong MVC tương tự như mô hình trong MVVM.

Trong MVC, quy trình được chia thành 3 trách nhiệm khác nhau:

  • View có trách nhiệm trình bày dữ liệu cho người dùng
  • Bộ điều khiển chịu trách nhiệm về lưu lượng trang
  • Một mô hình chịu trách nhiệm về logic kinh doanh

MVC không phù hợp lắm cho các ứng dụng web. Đó là một mẫu được giới thiệu bởi Smalltalk để tạo các ứng dụng máy tính để bàn. Một môi trường web hoạt động hoàn toàn khác nhau. Sẽ không có ý nghĩa gì khi sao chép một khái niệm 40 năm tuổi từ sự phát triển máy tính để bàn và dán nó vào một môi trường web. Tuy nhiên, rất nhiều người nghĩ rằng điều này là ổn, bởi vì ứng dụng của họ biên dịch và trả về các giá trị chính xác. Đó là, theo tôi, không đủ để tuyên bố một sự lựa chọn thiết kế nhất định là ok.

Một ví dụ về mô hình trong ứng dụng web có thể là:

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

Bộ điều khiển có thể sử dụng nó như thế này:

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

Các phương thức điều khiển và mô hình của bạn sẽ nhỏ, dễ kiểm tra và đi vào điểm chính.


Cảm ơn bạn đã hiểu biết sâu sắc về kiến ​​trúc MVVM, nhưng tại sao MVC không ổn? Lý luận của bạn là nghi vấn và nghi ngờ thiên vị. Cấp cho tôi không biết gì về MVVM, nhưng nếu một kiến ​​trúc như MVC có thể bắt chước hành vi mà không cần phải viết 50k dòng mã, thì vấn đề lớn là gì?
Đầu bếp_Code

@Chef_Code: Không có gì phải nghi ngờ hay thiên vị: chỉ cần đọc bài viết gốc về MVC. Quay trở lại nguồn tốt hơn nhiều so với việc mù quáng theo đàn mà không có câu hỏi (hay còn gọi là "thực hành tốt nhất"). MVC có nghĩa là cho các đơn vị nhỏ hơn nhiều: ví dụ: một nút trên màn hình bao gồm một mô hình, khung nhìn và bộ điều khiển. Trong Web-MVC, toàn bộ trang có bộ điều khiển, mô hình và khung nhìn. Mô hình và khung nhìn được cho là được kết nối, do đó những thay đổi trong mô hình sẽ được phản ánh ngay lập tức trong khung nhìn và ngược lại. Bắt chước là một vấn đề rất lớn. Một kiến ​​trúc không nên nói dối với các nhà phát triển của nó.
Jeroen

1
@jeroen Từ viết tắt MVC đã bị đánh cắp và đọc sai. Có MVC không có VM nhưng nó cũng không có Kho lưu trữ hoặc lớp dịch vụ và các đối tượng đó được sử dụng rộng rãi trong các trang web. Tôi tin rằng OP đang hỏi "làm thế nào để tôi giới thiệu và sử dụng VM trong MVC". Theo ý nghĩa mới của MVC, một mô hình không phải là nơi logic kinh doanh. Logic nghiệp vụ thuộc về một lớp dịch vụ cho web hoặc ứng dụng máy tính để bàn sử dụng MVC hoặc MVVM. Mô hình thuật ngữ mô tả các đối tượng kinh doanh được truyền đến / từ lớp dịch vụ. Các định nghĩa này rất khác với mô tả ban đầu của MVC.
Sam

1
@Sam Không phải tất cả mọi thứ là một phần của trang web, có thể được gọi là một phần của MVC. Không có ý nghĩa mới của MVC. Có ý nghĩa chính xác và "một cái gì đó hoàn toàn không liên quan mà mọi người nhầm lẫn với MVC" -meaning. Nói rằng mô hình chịu trách nhiệm về logic kinh doanh, không giống như logic kinh doanh được mã hóa trong mô hình. Hầu hết thời gian mô hình hoạt động như một mặt tiền cho ứng dụng.
Jeroen

Lỗ hổng chính mà tôi thấy trong Microsoft MVC là việc khóa Mô hình bằng Chế độ xem. Điều đó tự nó đánh bại toàn bộ mục đích của tất cả sự tách biệt này đang diễn ra trong các thiết kế của N-Tier trong 20 năm qua. Họ đã lãng phí thời gian của chúng tôi buộc chúng tôi sử dụng "WebForms" vào năm 2002, một mô hình lấy cảm hứng từ máy tính để bàn khác được đưa lên Thế giới Web. Bây giờ họ đã tung nó ra nhưng lại đưa ra một mô hình máy tính để bàn khác trên mô hình mới này cho nhà phát triển web. Trong thời gian đó, Google và những người khác đang xây dựng các mô hình phía khách hàng khổng lồ tách biệt tất cả. Tôi nghĩ rằng VB VBScript cũ từ năm 1998 là hệ thống phát triển web trung thực nhất của họ.
Stokely

11

Xem mô hình a là lớp đơn giản có thể chứa nhiều thuộc tính lớp. Chúng tôi sử dụng nó để kế thừa tất cả các thuộc tính cần thiết, ví dụ: tôi có hai lớp Sinh viên và Chủ đề

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

Bây giờ chúng tôi muốn hiển thị các bản ghi Tên và Chủ đề của sinh viên trong Chế độ xem (Trong MVC), nhưng không thể thêm nhiều hơn một lớp như:

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

đoạn mã trên sẽ gây ra lỗi ...

Bây giờ chúng tôi tạo một lớp và có thể đặt cho nó bất kỳ tên nào, nhưng định dạng "XyzViewModel" này sẽ giúp bạn dễ hiểu hơn. Đó là khái niệm thừa kế. Bây giờ chúng ta tạo một lớp thứ ba với tên sau:

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

Bây giờ chúng tôi sử dụng ViewModel này trong View

@model ProjectName.Model.StudentViewModel

Bây giờ chúng tôi có thể truy cập tất cả các thuộc tính của StudentViewModel và lớp kế thừa trong View.


10

Rất nhiều ví dụ lớn, hãy để tôi giải thích một cách rõ ràng và giòn.

ViewModel = Model được tạo để phục vụ cho view.

Chế độ xem ASP.NET MVC không thể có nhiều hơn một mô hình vì vậy nếu chúng ta cần hiển thị các thuộc tính từ nhiều hơn một mô hình vào chế độ xem thì không thể. ViewModel phục vụ mục đích này.

Mô hình khung nhìn là một lớp mô hình chỉ có thể chứa các thuộc tính được yêu cầu cho một khung nhìn. Nó cũng có thể chứa các thuộc tính từ nhiều hơn một thực thể (bảng) của cơ sở dữ liệu. Như tên cho thấy, mô hình này được tạo cụ thể cho các yêu cầu Xem.

Một số ví dụ về Mô hình xem bên dưới

  • Để liệt kê dữ liệu từ nhiều hơn các thực thể trong trang xem - chúng ta có thể tạo mô hình Chế độ xem và có các thuộc tính của tất cả các thực thể mà chúng ta muốn liệt kê dữ liệu. Tham gia các thực thể cơ sở dữ liệu đó và đặt thuộc tính mô hình Xem và quay lại Chế độ xem để hiển thị dữ liệu của các thực thể khác nhau trong một dạng bảng
  • Mô hình xem chỉ có thể xác định các trường cụ thể của một thực thể duy nhất được yêu cầu cho Chế độ xem.

ViewModel cũng có thể được sử dụng để chèn, cập nhật các bản ghi vào nhiều hơn một thực thể, tuy nhiên công dụng chính của ViewModel là hiển thị các cột từ nhiều thực thể (mô hình) vào một chế độ xem.

Cách tạo ViewModel giống như tạo Model, cách tạo view cho Viewmodel cũng giống như tạo view cho Model.

Đây là một ví dụ nhỏ về dữ liệu Danh sách bằng ViewModel .

Hy vọng điều này sẽ hữu ích.


6

ViewModel là giải pháp thay thế cho sự vụng về về mặt khái niệm của khung MVC. Nó đại diện cho lớp thứ 4 trong kiến ​​trúc Model-View-Controller 3 lớp. khi Mô hình (mô hình miền) không phù hợp, quá lớn (lớn hơn 2-3 trường) cho Chế độ xem, chúng tôi tạo ViewModel nhỏ hơn để chuyển nó sang Chế độ xem.


1

Một mô hình xem là một mô hình khái niệm của dữ liệu. Ví dụ, việc sử dụng nó là lấy một tập hợp con hoặc kết hợp dữ liệu từ các bảng khác nhau.

Bạn có thể chỉ muốn các thuộc tính cụ thể, vì vậy điều này cho phép bạn chỉ tải các thuộc tính đó chứ không phải các thuộc tính không cần thiết bổ sung


1
  • ViewModel chứa các trường được thể hiện trong dạng xem (đối với người trợ giúp LabelFor, EditorFor, DisplayFor)
  • ViewModel có thể có các quy tắc xác thực cụ thể bằng cách sử dụng chú thích dữ liệu hoặc IDataErrorInfo.
  • ViewModel có thể có nhiều thực thể hoặc đối tượng từ các mô hình dữ liệu hoặc nguồn dữ liệu khác nhau.

Thiết kế 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; } 
} 

Trình bày viewmodel trong khung nhìn

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

Làm việc với hành động

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. Trong ViewModel chỉ đặt những trường / dữ liệu mà bạn muốn hiển thị trên chế độ xem / trang.
  2. Do view xem lại các thuộc tính của ViewModel, do đó dễ dàng kết xuất và bảo trì.
  3. Sử dụng trình ánh xạ khi ViewModel trở nên phức tạp hơn.

1

View Model là lớp mà chúng ta có thể sử dụng để hiển thị dữ liệu trên View. Giả sử bạn có hai thực thể Place và PlaceC Category và bạn muốn truy cập dữ liệu từ cả hai thực thể bằng một mô hình duy nhất thì chúng tôi sử dụng 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; }
    }

Vì vậy, ở trên Ví dụ Địa điểm và Danh mục là hai thực thể khác nhau và chế độ xem PlaceC Category là ViewModel mà chúng ta có thể sử dụng trên View.


Ví dụ của bạn không quá rõ ràng. Điều được nêu ở trên là ViewModel kết nối dữ liệu với chế độ xem của nó. Nếu bạn nhìn vào ViewModels trong BlipAjax, bạn sẽ thấy các lớp hoàn toàn phù hợp với nó.
Herman Van Der Blom

0

Nếu bạn muốn nghiên cứu mã làm thế nào để thiết lập ứng dụng web "Đường cơ sở" với ViewModels, tôi có thể khuyên bạn nên tải xuống mã này trên GitHub: https://github.com/ajsaulsberry/BlipAjax . Tôi đã phát triển các ứng dụng doanh nghiệp lớn. Khi bạn làm điều này có vấn đề để thiết lập một kiến ​​trúc tốt xử lý tất cả chức năng "ViewModel" này. Tôi nghĩ với BlipAjax, bạn sẽ có một "đường cơ sở" rất tốt để bắt đầu. Nó chỉ là một trang web đơn giản, nhưng tuyệt vời trong sự đơn giản của nó. Tôi thích cách họ sử dụng ngôn ngữ tiếng Anh để chỉ ra những gì thực sự cần thiết trong ứng dụng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.