Co to jest ViewModel w MVC?


429

Jestem nowy w ASP.NET MVC. Mam problem ze zrozumieniem celu ViewModel.

Co to jest ViewModel i dlaczego potrzebujemy ViewModel dla aplikacji ASP.NET MVC?

Jeśli otrzymam dobry przykład jego działania i wyjaśnienia, byłoby lepiej.


4
Ten post jest tym, czego szukasz - „Co to jest ViewModel ASP.NET MVC?”
Yusubov,

6
Ten artykuł wygląda świetnie: rachelappel.com/…
Andrew

Odpowiedzi:


607

A view modelreprezentuje dane, które chcesz wyświetlić na widoku / stronie, niezależnie od tego, czy są one używane w przypadku tekstu statycznego, czy wartości wejściowych (takich jak pola tekstowe i listy rozwijane), które można dodać do bazy danych (lub edytować). To jest coś innego niż twojedomain model . Jest to model widoku.

Powiedzmy, że masz Employeeklasę reprezentującą model Twojej domeny pracowniczej i zawiera ona następujące właściwości (niepowtarzalny identyfikator, imię, nazwisko i data utworzenia):

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

     public string FirstName { get; set; }

     public string LastName { get; set; }

     public DateTime DateCreated { get; set; }
}

Modele widoku różnią się od modeli domen tym, że modele widoku zawierają tylko dane (reprezentowane przez właściwości), których chcesz użyć w swoim widoku. Załóżmy na przykład, że chcesz dodać nowy rekord pracownika, model widoku może wyglądać następująco:

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

     public string LastName { get; set; }
}

Jak widać, zawiera tylko dwie właściwości. Te dwie właściwości znajdują się również w modelu domeny pracowniczej. Dlaczego możesz o to zapytać? Idmoże nie być ustawiony z widoku, może być automatycznie generowany przez tabelę pracownika. I DateCreatedmoże być również ustawiony w procedurze przechowywanej lub w warstwie serwisowej aplikacji. Tak Idi DateCreatednie są potrzebne w modelu widoku. Możesz wyświetlić te dwie właściwości, gdy przeglądasz dane pracownika (pracownika, który został już przechwycony) jako tekst statyczny.

Podczas ładowania widoku / strony metoda tworzenia akcji w kontrolerze pracowniczym utworzy instancję tego modelu widoku, w razie potrzeby zapełni wszelkie pola, a następnie przekaże ten model widoku do widoku / strony:

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

Twój widok / strona może wyglądać tak (zakładając, że używasz ASP.NET MVCiRazor silnik wyświetlania):

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

Walidacja zostałaby zatem dokonana tylko w dniu FirstNamei LastName. Korzystanie z FluentValidation możesz mieć sprawdzanie poprawności w następujący sposób:

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

A w przypadku adnotacji danych może to wyglądać tak:

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

Najważniejszą rzeczą do zapamiętania jest to, że model widoku reprezentuje tylko dane, których chcesz użyć , nic więcej. Możesz sobie wyobrazić niepotrzebny kod i sprawdzanie poprawności, jeśli masz model domeny z 30 właściwościami i chcesz zaktualizować tylko jedną wartość. Biorąc pod uwagę ten scenariusz, miałbyś tylko tę jedną wartość / właściwość w modelu widoku, a nie wszystkie właściwości, które są w obiekcie domeny.

Model widoku może zawierać nie tylko dane z jednej tabeli bazy danych. Może łączyć dane z innej tabeli. Weź mój przykład powyżej dotyczący dodawania nowego rekordu pracownika. Oprócz dodania tylko imienia i nazwiska możesz również dodać dział pracownika. Ta lista działów będzie pochodzić z twojego Departmentsstołu. Teraz masz dane z tabel Employeesi Departmentsw jednym modelu widoku. Musisz wtedy dodać następujące dwie właściwości do modelu widoku i zapełnić go danymi:

public int DepartmentId { get; set; }

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

Podczas edycji danych pracownika (pracownika, który został już dodany do bazy danych) nie różni się on znacznie od mojego powyższego przykładu. Utwórz model widoku, nazwij go na przykład EditEmployeeViewModel. Posiadaj tylko dane, które chcesz edytować w tym modelu widoku, takie jak imię i nazwisko. Edytuj dane i kliknij przycisk Prześlij. Nie martwiłbym się zbytnio o to Idpole, ponieważ Idwartość prawdopodobnie będzie w adresie URL, na przykład:

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

Weź to Idi przekaż je do warstwy repozytorium wraz z wartościami imienia i nazwiska.

Podczas usuwania rekordu zwykle podążam tą samą ścieżką, co w przypadku modelu widoku edycji. Miałbym również adres URL, na przykład:

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

Kiedy widok ładuje się po raz pierwszy, pobierałem dane pracownika z bazy danych przy użyciu Id3. Korzystając z tego, wyświetlałem tekst statyczny na moim widoku / stronie, aby użytkownik mógł zobaczyć, który pracownik jest usuwany. Gdy użytkownik kliknie przycisk Usuń, po prostu użyję Idwartości 3 i przekażę ją do mojej warstwy repozytorium. Potrzebujesz tylkoId usunąć rekord z tabeli.

Inna kwestia: naprawdę nie potrzebujesz modelu widoku dla każdej akcji. Jeśli są to proste dane, dobrze byłoby użyć tylkoEmployeeViewModel . Jeśli jest to skomplikowana liczba wyświetleń / stron i różnią się one od siebie, sugeruję użycie osobnych modeli widoków dla każdego z nich.

Mam nadzieję, że rozwiąże to wszelkie nieporozumienia dotyczące modeli wyświetlania i modeli domen.


5
@Kenny: Więc pokaż to :) Chcę powiedzieć, że masz model domeny z 50 właściwościami, a twój widok musi wyświetlać tylko 5, wtedy nie ma sensu wysyłać wszystkich 50 właściwości tylko w celu wyświetlenia 5.
Brendan Vogt

5
@BrendanVogt - dobrze to wyjaśniłeś, ale nie rozumiem, jaki jest koszt „wysłania wszystkich 50 nieruchomości”. Inny kod został już utworzony Object Model, ze wszystkich 50 nieruchomości, a nie wydaje się warto utrzymać inną klasę po prostu nie wysłać 45 właściwości - szczególnie, jeśli może chcesz wysłać dowolną z tych 45 nieruchomości w przyszłości.
Kenny Evitt,

5
@BrendanVogt - Myślę, że może odpowiedź LukLeda pomaga mi zrozumieć, dlaczego mogą być one przydatne, zwłaszcza że ViewModel (może) „... łączyć wartości z różnych jednostek bazy danych” [gdzie zakładam, że wyrażenie jest tak samo prawdziwe: jednostki bazy danych ”, które należy zastąpić„ Obiektami modelu ”]. Ale jakie konkretne problemy miał rozwiązać ViewModels? Czy masz jakieś linki? Sam nic nie mogłem znaleźć. [I przepraszam, że wydaje mi się, że cię
zaczepiam

1
Właśnie usłyszałem, jak ktoś powiedział, że ViewModels to dobry sposób na wysłanie wielu kolekcji (lub właściwości między modelami) do jednego widoku bez konieczności umieszczania ich w viewBag. Dla mnie to ma sens.
Ayyash,

3
Przepraszam, że jestem krytyczny, ale ta odpowiedź jest niestety niepełna. Zdefiniowanie viewmodel jako tego, co potrzebujesz wyświetlać na swojej stronie, przypomina pytanie „Co to jest samochód?” i otrzymanie odpowiedzi „To nie jest samolot”. Cóż, to prawda, ale niezbyt pomocna. Bardziej poprawna definicja maszyny wirtualnej to „Wszystko, czego potrzebujesz do renderowania strony”. Jeśli przeczytałeś do końca, zidentyfikowałem komponenty potrzebne do prawidłowego i łatwego zbudowania maszyny wirtualnej, w wielu przypadkach wykorzystując istniejące modele domen i modele prezentacji.
Sam

133

Model widoku to klasa reprezentująca model danych używany w określonym widoku. Możemy użyć tej klasy jako modelu strony logowania:

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

Za pomocą tego modelu widoku można zdefiniować widok (silnik widoku Razor):

@model CamelTrap.Models.ViewModels.LoginPageVM

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

I działania:

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

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

Który daje ten wynik (ekran jest robiony po przesłaniu formularza, z komunikatami walidacyjnymi):

Jak widać, model widoku ma wiele ról:

  • Wyświetl modele dokumentuje widok, składając się tylko z pól, które są reprezentowane w widoku.
  • Wyświetl modele mogą zawierać określone reguły sprawdzania poprawności za pomocą adnotacji danych lub IDataErrorInfo.
  • Zobacz wzór określa, w jaki sposób powinien wyglądać widok (na LabelFor, EditorFor, DisplayForpomocników).
  • Wyświetl modele mogą łączyć wartości z różnych jednostek bazy danych.
  • Możesz łatwo określić szablony wyświetlania modeli widoku i ponownie użyć ich w wielu miejscach za pomocą pomocników DisplayFor lub EditorFor.

Kolejny przykład modelu widoku i jego pobierania: Chcemy wyświetlać podstawowe dane użytkownika, jego uprawnienia i nazwę użytkownika. Tworzymy specjalny model widoku, który zawiera tylko wymagane pola. Pobieramy dane z różnych jednostek z bazy danych, ale widok jest świadomy tylko klasy modelu widoku:

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

Wyszukiwanie:

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
} 

I cienki user.Mother.FirstName + "" + user.Mother.LastName powinien być zrobiony w View Model End. Cała logika powinna być wykonywana na końcu widoku modelu.
Kurkula,

3
@Chandana: Wierzę, że prostego konkatenacji można dokonać w modelu widoku. Nie ma powodu, aby ujawniać dwa pola, jeśli mają one być prezentowane razem.
LukLed

82

Edycja: Zaktualizowałem tę odpowiedź na moim blogu:

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

Moja odpowiedź jest nieco długa, ale uważam, że ważne jest porównanie modeli widoków z innymi typami często używanych modeli, aby zrozumieć, dlaczego są one różne i dlaczego są konieczne.

Podsumowując i bezpośrednio odpowiadając na zadane pytanie:

Mówiąc ogólnie, model widoku jest obiektem, który zawiera wszystkie właściwości i metody niezbędne do renderowania widoku. Wyświetl właściwości modelu są często powiązane z obiektami danych, takimi jak klienci i zamówienia, a ponadto zawierają również właściwości związane ze stroną lub samą aplikacją, takie jak nazwa użytkownika, nazwa aplikacji itp. Wyświetl modele zapewniają wygodny obiekt, który można przekazać do silnika renderującego utwórz stronę HTML. Jednym z wielu powodów korzystania z modelu widoku jest to, że modele widoku zapewniają sposób testowania jednostkowego niektórych zadań prezentacji, takich jak obsługa danych wejściowych użytkownika, sprawdzanie poprawności danych, pobieranie danych do wyświetlenia itp.

Oto porównanie modeli jednostek (np. Modeli DTO, np. Modeli), modeli prezentacji i modeli widoków.

Obiekty przesyłania danych znane również jako „Model”

Obiekt przenoszenia danych (DTO) to klasa o właściwościach pasujących do schematu tabeli w bazie danych. DTO są nazwane ze względu na ich powszechne użycie do przesyłania danych do iz magazynu danych.
Charakterystyka DTO:

• Są obiektami biznesowymi - ich definicja zależy od danych aplikacji.

• Zwykle zawierają tylko właściwości - bez kodu.

• Używany głównie do transportu danych do iz bazy danych.

• Właściwości dokładnie lub ściśle pasują do pól w określonej tabeli w magazynie danych.

Tabele bazy danych są zwykle znormalizowane, dlatego DTO są zwykle znormalizowane. To sprawia, że ​​mają ograniczone zastosowanie do prezentacji danych. Jednak w przypadku niektórych prostych struktur danych często mają się całkiem dobrze.

Oto dwa przykłady tego, jak mogą wyglądać 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; }
}

Modele prezentacji

Model prezentacji to narzędzie klasą używaną do renderowania danych na ekranie lub w raporcie. Modele prezentacji są zwykle używane do modelowania złożonych struktur danych, które składają się z danych z wielu DTO. Modele prezentacji często przedstawiają zdenormalizowany widok danych.

Charakterystyka modeli prezentacji:

• Są obiektami biznesowymi - ich definicja zależy od danych aplikacji.

• Zawierają głównie właściwości. Kod zazwyczaj ogranicza się do formatowania danych lub konwersji do lub z DTO. Modele prezentacji nie powinny zawierać logiki biznesowej.

• Często przedstawiają zdenormalizowany widok danych. Oznacza to, że często łączą właściwości z wielu DTO.

• Często zawierają właściwości innego typu bazowego niż DTO. Na przykład kwoty w dolarach mogą być reprezentowane jako ciągi znaków, dzięki czemu mogą zawierać przecinki i symbol waluty.

• Często definiowane przez sposób ich użycia, a także przez ich cechy obiektu. Innymi słowy, proste DTO, które jest używane jako model podkładu do renderowania siatki, jest w rzeczywistości również modelem prezentacji w kontekście tej siatki.

Modele prezentacji są używane „w razie potrzeby” i „w razie potrzeby” (podczas gdy DTO są zwykle powiązane ze schematem bazy danych). Model prezentacji może służyć do modelowania danych dla całej strony, siatki na stronie lub listy rozwijanej na siatce na stronie. Modele prezentacji często zawierają właściwości, które są innymi modelami prezentacji. Modele prezentacji są często budowane w celu jednorazowego użytku, takiego jak renderowanie określonej siatki na jednej stronie.

Przykładowy model prezentacji:

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

Zobacz modele

Model widoku jest podobny do modelu prezentacji, ponieważ jest klasą pomocniczą do renderowania widoku. Jednak bardzo różni się od modelu prezentacji lub DTO pod względem budowy. Modele widoku często zawierają te same właściwości co modele prezentacji i DTO, dlatego często są mylone.

Charakterystyka modeli widoków:

• Są jedynym źródłem danych używanych do renderowania strony lub ekranu. Zwykle oznacza to, że model widoku ujawni każdą właściwość, której każda formant na stronie będzie musiał poprawnie wyrenderować. Utworzenie modelu widoku pojedynczego źródła danych dla widoku znacznie poprawia jego możliwości i wartość do testowania jednostkowego.

• Są obiektami złożonymi, które zawierają właściwości składające się z danych aplikacji oraz właściwości używane przez kod aplikacji. Ta cecha jest kluczowa przy projektowaniu modelu widoku do ponownego użycia i została omówiona w poniższych przykładach.

• Zawierać kod aplikacji. Wyświetl modele zwykle zawierają metody wywoływane podczas renderowania i podczas interakcji użytkownika ze stroną. Ten kod zazwyczaj dotyczy obsługi zdarzeń, animacji, widoczności elementów sterujących, stylizacji itp.

• Zawierają kod wywołujący usługi biznesowe w celu odzyskania danych lub wysłania ich do serwera bazy danych. Ten kod jest często błędnie umieszczony w kontrolerze. Wywoływanie usług biznesowych z kontrolera zwykle ogranicza przydatność modelu widoku do testowania jednostkowego. Żeby było jasne, same modele przeglądania nie powinny zawierać logiki biznesowej, ale powinny nawiązywać połączenia z usługami, które zawierają logikę biznesową.

• Często zawierają właściwości, które są innymi modelami widoku dla innych stron lub ekranów.

• Są zapisywane „na stronę” lub „na ekran”. Unikalny model widoku jest zwykle zapisywany dla każdej strony lub ekranu w aplikacji.

• Zwykle wywodzą się z klasy podstawowej, ponieważ większość stron i ekranów ma wspólne właściwości.

Zobacz skład modelu

Jak wspomniano wcześniej, modele widoków są obiektami złożonymi, ponieważ łączą właściwości aplikacji i właściwości danych biznesowych w jednym obiekcie. Przykłady często używanych właściwości aplikacji używanych w modelach widoku to:

• Właściwości używane do wyświetlania stanu aplikacji, takie jak komunikaty o błędach, nazwa użytkownika, status itp.

• Właściwości używane do formatowania, wyświetlania, stylizacji lub animacji formantów.

• Właściwości używane do wiązania danych, takie jak obiekty listy i właściwości przechowujące dane pośrednie, które są wprowadzane przez użytkownika.

Poniższe przykłady pokazują, dlaczego złożona natura modeli widoków jest ważna i jak najlepiej skonstruować model widoku tak wydajny i wielokrotnego użytku.

Załóżmy, że piszemy aplikację internetową. Jednym z wymagań projektu aplikacji jest to, że tytuł strony, nazwa użytkownika i nazwa aplikacji muszą być wyświetlane na każdej stronie. Jeśli chcemy utworzyć stronę do wyświetlania obiektu zamówienia prezentacji, możemy zmodyfikować model prezentacji w następujący sposób:

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

Ten projekt może działać… ale co, jeśli chcemy stworzyć stronę, na której będzie wyświetlana lista zamówień? Właściwości PageTitle, UserName i ApplicationName zostaną powtórzone i nie będą działać. A co jeśli chcemy zdefiniować logikę na poziomie strony w konstruktorze klasy? Nie możemy tego dłużej robić, jeśli utworzymy instancję dla każdego zamówienia, które zostanie wyświetlone.

Skład nad dziedziczeniem

Oto sposób, w jaki możemy zmienić fakturę modelu prezentacji zamówienia, tak aby stał się on prawdziwym modelem widoku i będzie przydatny do wyświetlania pojedynczego obiektu PresentationOrder lub kolekcji obiektów 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; }
}

Patrząc na powyższe dwie klasy, widzimy, że jednym ze sposobów myślenia o modelu widoku jest to, że jest to model prezentacji, który zawiera inny model prezentacji jako właściwość. Model prezentacji najwyższego poziomu (tj. Model widoku) zawiera właściwości istotne dla strony lub aplikacji, podczas gdy model prezentacji (właściwość) zawiera właściwości istotne dla danych aplikacji.

Możemy pójść o krok dalej i stworzyć klasę modelu widoku podstawowego, która może być używana nie tylko w przypadku PresentationOrders, ale także w każdej innej klasie:

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

Teraz możemy uprościć naszą PresentationOrderVM w następujący sposób:

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

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

Możemy sprawić, że nasz BaseViewModel będzie jeszcze bardziej przydatny, czyniąc go ogólnym:

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

Teraz nasze wdrożenia są łatwe:

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

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

2
Sam dziękuję !! pomogło mi to w pełni zrozumieć wieloaspektowy byt, którym jest: Model widoku. Jestem studentem college'u, właśnie uczę się architektury MVC, a to wyjaśniło kilka przydatnych funkcji, które są udostępniane programistom. Gdybym mógł, umieściłbym gwiazdkę obok twojej odpowiedzi.
Chef_Code

1
@Sam „Wyświetl modele często zawierają te same właściwości co modele prezentacji i DTO, dlatego często są mylone ze sobą”. Czy to oznacza, że ​​są one powszechnie używane zamiast modeli prezentacji, czy też mają zawierać modele prezentacji / dtos?
Alexander Derck

2
@AlexanderDerck Są one wykorzystywane do różnych celów. Są mylone jeden do drugiego (przez pomyłkę). Nie, zazwyczaj nie używasz modelu wstępnego zamiast modelu widoku. Znacznie bardziej powszechne jest to, że VM „zawiera” model prezentacji, tj. MyViewModel<MyPresModel>
Sam

2
@Sam Zakładając, że obiekty modelowe są obiektami żywymi, np. Modelami nhibernate .. więc mając BusinessObject, nie wystawiamy obiektów modelowych / żywych bezpośrednio na widok? tzn. obiekt biznesowy może służyć do bezpośredniej modyfikacji stanu bazy danych? A co z modelami widoków zagnieżdżonych? Wymagałoby to wielu właściwości obiektu biznesowego, prawda?
Muhammad Ali,

22

Jeśli masz właściwości specyficzne dla widoku i niezwiązane z magazynem DB / Service / Data, dobrą praktyką jest używanie ViewModels. Powiedzmy, że chcesz pozostawić zaznaczone pole wyboru na podstawie pola DB (lub dwóch), ale samo pole DB nie jest wartością logiczną. Chociaż możliwe jest utworzenie tych właściwości w samym Modelu i ukrycie go przed powiązaniem z danymi, możesz nie chcieć zaśmiecać Modelu w zależności od ilości takich pól i transakcji.

Jeśli jest za mało danych i / lub transformacji specyficznych dla widoku, możesz użyć samego modelu


19

Nie przeczytałem wszystkich postów, ale wydaje się, że w każdej odpowiedzi brakuje jednej koncepcji, która naprawdę pomogła mi „zdobyć” ...

Jeśli model jest podobny do tabeli bazy danych , to ViewModel jest podobny do widoku bazy danych - widok zwykle zwraca albo małe ilości danych z jednej tabeli, albo złożone zestawy danych z wielu tabel (złączeń).

Używam ViewModels do przekazywania informacji do widoku / formularza, a następnie przesyłam te dane do prawidłowego modelu, gdy formularz przesyła z powrotem do kontrolera - bardzo przydatne do przechowywania list (IEnumerable).


11

MVC nie ma modelu widoku: ma model, widok i kontroler. Viewmodel jest częścią MVVM (Model-View-Viewmodel). MVVM pochodzi z Modelu prezentacji i jest spopularyzowany w WPF. Powinien również istnieć model w MVVM, ale większość ludzi całkowicie nie rozumie sensu tego wzoru i będą mieli tylko widok i model widoku. Model w MVC jest podobny do modelu w MVVM.

W MVC proces dzieli się na 3 różne obowiązki:

  • View jest odpowiedzialny za przedstawienie danych użytkownikowi
  • Kontroler odpowiada za przepływ strony
  • Model odpowiada za logikę biznesową

MVC nie jest bardzo odpowiedni do aplikacji internetowych. Jest to wzór wprowadzony przez Smalltalk do tworzenia aplikacji komputerowych. Środowisko sieciowe zachowuje się zupełnie inaczej. Nie ma sensu kopiować 40-letniej koncepcji z pulpitu i wklejać ją do środowiska sieciowego. Jednak wiele osób uważa, że ​​jest to w porządku, ponieważ ich aplikacja kompiluje się i zwraca prawidłowe wartości. To moim zdaniem nie wystarczy, aby zadeklarować pewien wybór projektu jako w porządku.

Przykładem modelu w aplikacji internetowej może być:

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

Kontroler może używać tego w następujący sposób:

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

Metody kontrolera i modele będą małe, łatwe do przetestowania i do rzeczy.


Dziękujemy za wgląd w architekturę MVVM, ale dlaczego MVC nie jest OK? Twoje rozumowanie jest wątpliwe i podejrzewa o faworyzowanie. To prawda, że ​​nic nie wiem o MVVM, ale jeśli architektura taka jak MVC może naśladować zachowanie bez konieczności pisania 50 000 wierszy kodu, to o co chodzi?
Chef_Code

@Chef_Code: To nie jest wątpliwe ani faworyzowane: po prostu przeczytaj oryginalny artykuł o MVC. Powrót do źródła jest znacznie lepszy niż ślepe podążanie za stadem bez pytania (inaczej „najlepsze praktyki”). MVC jest przeznaczony dla znacznie mniejszych jednostek: np. Przycisk na ekranie składa się z modelu, widoku i kontrolera. W Web-MVC cała strona ma kontroler, model i widok. Model i widok powinny być ze sobą połączone, aby zmiany w modelu były natychmiast odzwierciedlane w widoku i odwrotnie. Naśladowanie to bardzo ważna sprawa. Architektura nie powinna kłamać deweloperom.
Jeroen

1
@jeroen Akronim MVC został skradziony i zniekształcony. Tak, MVC nie ma maszyny wirtualnej, ale nie ma też repozytorium ani warstwy usług, a obiekty te są szeroko stosowane w witrynach internetowych. Wierzę, że OP pyta „jak wprowadzić i używać VM w MVC”. W nowym znaczeniu MVC model nie należy do logiki biznesowej. Logika biznesowa należy do warstwy usług dla sieci lub aplikacji komputerowej wykorzystującej MVC lub MVVM. Termin model opisuje obiekty biznesowe przekazywane do / z warstwy usługi. Te definicje znacznie różnią się od oryginalnego opisu MVC.
Sam

1
@Sam Nie wszystko, co jest częścią strony internetowej, można nazwać częścią MVC. Nie ma nowego znaczenia MVC. Istnieje prawidłowe znaczenie i „coś zupełnie niezwiązanego z tym, co ludzie mylą z MVC”. Powiedzenie, że model jest odpowiedzialny za logikę biznesową, nie jest tym samym, co logika biznesowa jest zakodowana w modelu. Przez większość czasu model działa jako fasada aplikacji.
Jeroen

Główną wadą, którą widzę w MVC Microsoftu, jest blokowanie modelu z widokiem. To samo w sobie pokonuje cały cel tej separacji, która miała miejsce w projektach N-Tier przez ostatnie 20 lat. Zmarnowali czas, zmuszając nas do korzystania z „WebForms” w 2002 roku, który był kolejnym modelem inspirowanym Desktopem, wprowadzonym do Web World. Teraz wyrzucili to, ale jeszcze raz podnieśli kolejny model pulpitu na tym nowym paradygmacie dla web dev. W międzyczasie Google i inni budują gigantyczne modele po stronie klienta, które to wszystko oddzielają. Myślę, że stary ASP VBScript z 1998 roku był ich najprawdziwszym systemem webowym.
Stokely

11

Wyświetl model a to prosta klasa, która może zawierać więcej niż jedną właściwość klasy. Używamy go do dziedziczenia wszystkich wymaganych właściwości, np. Mam dwie klasy Student i Przedmiot

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

Teraz chcemy wyświetlać rekordy imię i nazwisko ucznia oraz nazwę przedmiotu w widoku (w MVC), ale nie można dodać więcej niż jednej klasy, np .:

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

powyższy kod zgłosi błąd ...

Teraz tworzymy jedną klasę i możemy nadać jej dowolną nazwę, ale ten format „XyzViewModel” ułatwi zrozumienie. Jest to koncepcja dziedziczenia. Teraz tworzymy trzecią klasę o następującej nazwie:

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

Teraz używamy tego ViewModel w widoku

@model ProjectName.Model.StudentViewModel

Teraz jesteśmy w stanie uzyskać dostęp do wszystkich właściwości StudentViewModel i odziedziczonej klasy w View.


10

Wiele dużych przykładów, wyjaśnię w jasny i chrupiący sposób.

ViewModel = Model utworzony w celu obsługi widoku.

Widok ASP.NET MVC nie może mieć więcej niż jednego modelu, więc jeśli musimy wyświetlić właściwości z więcej niż jednego modelu w widoku, nie jest to możliwe. ViewModel służy temu celowi.

Model widoku to klasa modelu, która może przechowywać tylko te właściwości, które są wymagane dla widoku. Może także zawierać właściwości z więcej niż jednego elementu (tabel) bazy danych. Jak sama nazwa wskazuje, ten model jest tworzony zgodnie z wymaganiami widoku.

Kilka przykładów widoków modeli znajduje się poniżej

  • Aby wyświetlić dane z więcej niż encji na stronie widoku - możemy stworzyć model View i mieć właściwości wszystkich encji, dla których chcemy wyświetlić dane. Dołącz do tych jednostek bazy danych i ustaw Wyświetl właściwości modelu i wróć do Widoku, aby wyświetlić dane różnych encji w jednej formie tabelarycznej
  • Model widoku może definiować tylko określone pola pojedynczego elementu, które są wymagane dla widoku.

ViewModel może być również używany do wstawiania, aktualizowania rekordów w więcej niż jednej encji, jednak głównym zastosowaniem ViewModel jest wyświetlanie kolumn z wielu encji (modelu) w jednym widoku.

Sposób tworzenia ViewModel jest taki sam jak tworzenie modelu, sposób tworzenia widoku dla Viewmodel jest taki sam jak tworzenie widoku dla modelu.

Oto mały przykład danych listy przy użyciu ViewModel .

Mam nadzieję, że to się przyda.


6

ViewModel to obejście, które załatwia konceptualną niezręczność frameworka MVC. Reprezentuje 4. warstwę w 3-warstwowej architekturze Model-View-Controller. gdy Model (model domeny) nie jest odpowiedni, zbyt duży (większy niż 2-3 pola) dla Widoku, tworzymy mniejszy ViewModel, aby przekazać go do Widoku.


1

Model widoku jest koncepcyjnym modelem danych. Jego zastosowaniem jest na przykład uzyskanie podzbioru lub połączenie danych z różnych tabel.

Możesz chcieć tylko określonych właściwości, więc pozwala to tylko ładować te, a nie dodatkowe niepotrzebne właściwości


1
  • ViewModel zawiera pola, które są reprezentowane w widoku (dla pomocników LabelFor, EditorFor, DisplayFor)
  • ViewModel może mieć określone reguły sprawdzania poprawności za pomocą adnotacji danych lub IDataErrorInfo.
  • ViewModel może mieć wiele encji lub obiektów z różnych modeli danych lub źródła danych.

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

Prezentacja modelu widoku w widoku

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

Praca z działaniem

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. W ViewModel umieść tylko te pola / dane, które chcesz wyświetlić na widoku / stronie.
  2. Ponieważ widok reprezentuje właściwości ViewModel, dlatego jest łatwy do renderowania i konserwacji.
  3. Użyj mapera, gdy ViewModel stanie się bardziej złożony.

1

View Model to klasa, której możemy użyć do renderowania danych w View. Załóżmy, że masz dwa podmioty Place i PlaceCategory i chcesz uzyskać dostęp do danych z obu podmiotów za pomocą jednego modelu, a następnie korzystamy z 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; }
    }

Tak więc w powyższym przykładzie Place i Category to dwa różne podmioty, a ViewCodel PlaceCategory to ViewModel, którego możemy użyć w View.


Twoje przykłady nie są tak jasne. Powyżej stwierdzono, że ViewModel łączy dane z jego widokiem. Jeśli spojrzysz na ViewModels w BlipAjax, zobaczysz klasy, które idealnie do tego nadają.
Herman Van Der Blom

0

Jeśli chcesz przestudiować kod, jak skonfigurować aplikację internetową „Baseline” za pomocą ViewModels, radzę pobrać ten kod na GitHub: https://github.com/ajsaulsberry/BlipAjax . Opracowałem aplikacje dla dużych przedsiębiorstw. Gdy to zrobisz, problematyczne jest ustawienie dobrej architektury, która obsługuje wszystkie funkcje „ViewModel”. Myślę, że dzięki BlipAjax będziesz miał bardzo dobrą „linię bazową” na początek. To tylko prosta strona internetowa, ale świetna w swojej prostocie. Podoba mi się sposób, w jaki używali języka angielskiego, aby wskazać, co jest naprawdę potrzebne w aplikacji.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.