Najlepsze Praktyki ViewModel

Z tego pytania, wygląda na to, że sensowne jest stworzenie kontrolera ViewModel , który dokładniej odzwierciedla model, który Widok próbuje wyświetlić, ale jestem ciekaw niektórych konwencji (jestem nowy w wzorze MVC, jeśli nie było to oczywiste).

W zasadzie miałem następujące pytania:

  1. normalnie lubię mieć jedną klasę / plik. Czy to ma sens z ViewModel jeśli jest tworzony tylko po to, aby przekazać dane z kontrolera do widoku?
  2. Jeśli ViewModel należy do jego własnego pliku, a Ty używasz struktury katalogu/projektu do oddzielania rzeczy, gdzie należy Plik ViewModel ? W katalogu Controllers ?

To na razie tyle. Może mam jeszcze kilka pytań, ale to mnie dręczy przez ostatnią godzinę i mogę znaleźć spójne wskazówki gdzie indziej.

EDIT: Szukam w przykładowej aplikacji NerdDinner Na CodePlex wygląda na to, że Viewmodele są częścią kontrolerów , ale nadal czuję się nieswojo, że nie ma ich we własnych plikach.

Author: Community, 2009-03-20

11 answers

Tworzę coś, co nazywam "ViewModel" dla każdego widoku. Umieściłem je w folderze o nazwie ViewModels w moim projekcie MVC Web. Nazywam je po kontrolerze i akcji (lub widoku), które reprezentują. Jeśli więc muszę przekazać dane do widoku rejestracji na kontrolerze członkostwa, tworzę MembershipSignUpViewModel.klasy cs i umieścić go w folderze ViewModels.

Następnie dodaję niezbędne właściwości i metody ułatwiające transfer danych z kontrolera do widoku. Używam Automapera do przejdź z mojego modelu ViewModel do modelu domeny i z powrotem, jeśli to konieczne.

Działa to również dobrze w przypadku modeli złożonych, które zawierają właściwości, które są typu innych modeli. Na przykład, jeśli masz 5 widżetów na stronie indeks w kontrolerze członkostwa i utworzyłeś model widoku dla każdego widoku częściowego - jak przekazać dane z akcji indeks do częściowych? Dodajesz właściwość do MembershipIndexViewModel typu MyPartialViewModel i podczas renderowania częściowy, który zdasz w modelu.MyPartialViewModel.

W ten sposób można dostosować właściwości częściowego modelu widoku bez konieczności zmiany widoku indeksu. To wciąż przechodzi w modelu.MyPartialViewModel więc jest mniejsza szansa, że będziesz musiał przejść przez cały łańcuch częściowych, aby coś naprawić, gdy wszystko, co robisz, to dodawanie właściwości do częściowego modelu widoku.

Dodam również przestrzeń nazw " MyProject.Www.ViewModels " do sieci.config so aby umożliwić mi odwoływanie się do nich w dowolnym widoku bez dodawania wyraźnej deklaracji importu w każdym widoku. To sprawia, że jest trochę czystsze.

 208
Author: Ryan Montgomery,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-04 20:07:10

Oddzielanie klas według kategorii (Kontrolery, Viewmodele, filtry itp.) jest nonsensem.

Jeśli chcesz napisać kod dla sekcji Home na swojej stronie internetowej ( / ), utwórz folder o nazwie Home i umieść tam HomeController, IndexViewModel, AboutViewModel, itd. i wszystkie pokrewne klasy używane przez akcje domowe.

Jeśli masz współdzielone klasy, takie jak ApplicationController, możesz umieścić je w katalogu głównym projektu.

Dlaczego oddzielać rzeczy, które są powiązane (HomeController, IndexViewModel) i trzymać rzeczy razem, które nie mają żadnej relacji (HomeController, AccountController)?


Napisałem post na blogu na ten temat.

 119
Author: Max Toro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-01 03:49:31

Przechowuję klasy aplikacji w podfolderze o nazwie "Core" (lub oddzielnej bibliotece klas) i używam tych samych metod, co przykładowa aplikacja KIGG, ale z niewielkimi zmianami, aby moje aplikacje były bardziej suche.

Tworzę klasę BaseViewData w /Core/ViewData / gdzie przechowuję wspólne właściwości witryny.

Następnie tworzę wszystkie moje klasy view ViewData w tym samym folderze, które następnie wywodzą się z BaseViewData i mają specyficzny widok właściwości.

Następnie tworzę ApplicationController, z którego wywodzą się wszystkie moje Kontrolery. ApplicationController posiada ogólną metodę GetViewData w następujący sposób:

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

Wreszcie, w mojej akcji kontrolera wykonuję następujące czynności, aby zbudować mój Model ViewData

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

Myślę, że to działa naprawdę dobrze i utrzymuje twoje poglądy schludny i kontrolery chudy.

 20
Author: Mark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-25 21:53:06

Klasa ViewModel służy do hermetyzacji wielu fragmentów danych reprezentowanych przez instancje klas w jeden łatwy do zarządzania obiekt, który można przekazać do widoku.

Miałoby sens posiadanie klas ViewModel w ich własnych plikach, we własnym katalogu. W moich projektach mam podkatalog folderu Models o nazwie ViewModels. Tam mieszkają moje Viewmodele (np. ProductViewModel.cs).

 14
Author: JMS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-19 22:34:00

Nie ma dobrego miejsca na trzymanie swoich Modeli. Można je przechowywać w osobnym złożeniu, jeśli projekt jest duży i istnieje wiele ViewModels (obiektów transferu danych). Możesz również przechowywać je w oddzielnym folderze projektu witryny. Na przykład, w Oxite są one umieszczone w projekcie Oxite, który zawiera również wiele różnych klas. Kontrolery w Oxite są przenoszone do osobnego projektu, a widoki są również w osobnym projekcie.
W CodeCampServer Viewmodele mają nazwę * Form i są one umieszczane w projekcie interfejsu użytkownika w folderze modele.
W projekcie MvcPress są one umieszczone w projekcie Data, który zawiera również cały kod do pracy z bazą danych i trochę więcej (ale nie polecam tego podejścia, to tylko przykład)
Więc widać, że jest wiele punktów widzenia. Zazwyczaj trzymam moje ViewModels (obiekty DTO) w projekcie witryny. Ale kiedy mam więcej niż 10 modeli, wolę przenieść je do osobnego montażu. Zazwyczaj w tym przypadku przenoszę Kontrolery do oddzielnego montaż również.
Kolejnym pytaniem jest to, jak łatwo mapować wszystkie dane z modelu do modelu widoku. Proponuję zajrzeć do biblioteki AutoMapper . Bardzo mi się podoba, robi za mnie całą brudną robotę.
Proponuję też przyjrzeć się projektowi SharpArchitecture. Zapewnia bardzo dobrą architekturę dla projektów i zawiera wiele fajnych frameworków i przewodników oraz świetną społeczność.

 12
Author: zihotki,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-25 21:47:59

Oto fragment kodu z moich najlepszych praktyk:

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

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

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

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

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

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

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

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

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

            userService.Save(editBuilder.BuilEntity(input));
            return RedirectToAction("Index");
        }
}
 6
Author: Omu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-08 06:31:38

Wrzucamy wszystkie nasze ViewModels do folderu Models (cała nasza logika biznesowa znajduje się w oddzielnym projekcie ServiceLayer)

 5
Author: Beep beep,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-23 04:07:18

Osobiście sugerowałbym, że jeśli ViewModel nie jest trywialny to należy użyć osobnej klasy.

Jeśli masz więcej niż jeden model widoku, sugeruję, aby sensowne było partycjonowanie go przynajmniej w katalogu. jeśli model widoku zostanie później udostępniony, to przestrzeń nazw sugerowana w katalogu ułatwia przeniesienie do nowego złożenia.

 4
Author: Preet Sangha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-19 22:21:48

W naszym przypadku mamy modele wraz ze sterownikami w projekcie oddzielonym od widoków.

Z reguły staraliśmy się poruszać i unikać większości ViewData ["..."] rzeczy do ViewModel, dzięki czemu unikamy odlewów i magicznych sznurków, co jest dobrą rzeczą.

ViewModel posiada również pewne typowe właściwości, takie jak informacje o stronicowaniu list lub informacje o nagłówku strony, aby narysować bułkę tartą i tytuły. W tym momencie klasa bazowa trzyma za dużo informacje moim zdaniem i możemy podzielić je na trzy części, najbardziej podstawowe i niezbędne informacje dla 99% stron w modelu widoku bazowego, a następnie model dla list i model dla formularzy, które posiadają konkretne dane dla tego scenariuszy i dziedziczą z podstawowego.

Wreszcie, wdrażamy model widoku dla każdego podmiotu, aby poradzić sobie z konkretnymi informacjami.

 2
Author: Marc Climent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-21 15:49:20

Kod w kontrolerze:

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

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

        #region save

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

        #endregion
    }

Kod w modelu widoku:

public class EntryEditViewModel
    {
        #region Private Variables for Properties

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

        #endregion

        #region Public Properties

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

        public StatusList StatusList
        {
            get { return _statusList; }
        }

        #endregion

        #region constructor(s)

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

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

        #endregion       
    }

Projekty:

  • DevJet.Web ( the ASP.NET MVC web projekt)

  • DevJet.Www.App.Słownik (a oddzielny projekt biblioteki klas)

    W tym projekcie wykonałem kilka folderów takich jak: DAL, BLL, BO, VM (folder dla modeli widoku)

 0
Author: C.T.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-18 04:27:17

Utwórz klasę bazową modelu widoku, która ma powszechnie wymagane właściwości, takie jak wynik operacji i dane kontekstowe, możesz również umieścić bieżące dane użytkownika i role

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

W base controller class have a method like PopulateViewModelBase () ta metoda wypełni dane kontekstowe i role użytkownika. HasError i ErrorMessage ustawiają te właściwości, jeśli istnieje wyjątek podczas pobierania danych z service / db. Zwiąż te właściwości w widoku, aby wyświetlić błąd. Role użytkowników mogą być służy do wyświetlania sekcji Ukryj w widoku na podstawie ról.

Aby wypełniać modele widoków w różnych akcjach get, można uczynić je spójnymi, mając kontroler bazowy z abstrakcyjną metodą FillModel

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

W kontrolerach

class MyController :Controller 
{

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

ViewModelBase FillModel() 
{ 
    ViewModelBase  model=;
    string currentAction = HttpContext.Current.Request.RequestContext.RouteData.Values["action"].ToString(); 
 try 
{ 
   switch(currentAction) 
{  
   case "Index": 
   model= GetCustomerData(); 
   break;
   // fill model logic for other actions 
}
}
catch(Exception ex) 
{
   model.HasError=true;
   model.ErrorMessage=ex.Message;
}
//fill common properties 
base.PopulateViewModelBase(model);
return model;
}
}
 0
Author: Ajay Kelkar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-06 06:49:53