MVVM v asp.net MVC

V minulém příspěvku, kdy jsem se věnoval mým postřehů z použití asp.net MVC jsem též zmínil možné použití MVVM přístupu a analogii použití k desktopovým aplikacím.

Použití MVVM – Model View ViewModel vzoru

Hlavní motivací pro použití tohoto vzoru pro mě bylo, jak jsem již minule zmínil v odlehčení Controlleru a zjednodušení View, kde jsem nechtěl řešit implementační detaily, především pak samotné vyhodnocování podmínek.

Zároveň s tím jsem se však dostal v prvních krocích do menších komplikací, neboť jsem byl postaven před otázku rozumného vyřešení předávání některých potřebných objektů do ViewModelu (VM) a zároveň jsem si chtěl ponechat volnost a možnost testování VM. Pro lepší přehled zmíním použití jednorázového uložiště dat v podobě TempDataDictionary.

IoC/DI container

Jelikož jsem si velice oblíbil použití IoC/DI containeru konstruoval jsem aplikaci tak, abych tento přístup mohl využít i při tvorbě asp.net MVC aplikace. Jako vhodnou volbou se tak jevilo použití MVCContrib projektu, který nabízel nativní podporu pro všechny rozšířenější implementace. Moji volbou se stal Unity vyvíjený skupinou pattern & practices.

V konečném důsledku mi právě použití DI containeru pomohlo v tom, jak vyřešit právě ono předávání (injectování) objektů do VM. Samozřejmě by to šlo zařídit i jinak, ale přeci jen mi tento způsob přijde elegantnější a pro vývojáře jednodušší – nemusí myslet na to, co musí udělat a co případně musí zavolat.

Když už jsem zmínil právě ono jednorázové uložiště dat (TempDataDictionary), chvíli jsem přemýšlel, jak jej vhodně předat, nakonec mi jako vhodný způsob přišlo použít existující instanci tohoto objektu získaného v okamžiku vytvoření Controlleru a v přepsané třídě poděděné od DefaultControllerFactory jsem tuto instanci vložil do containeru – samozřejmě s platností pouze pro daný web request. Zde tedy bylo nutné ještě napsat si vlastní WebRequestLifetimeManager, neboť Unity ve své výchozí instalaci zná pouze dva LifetimeManagery a samozřejmě ani jeden z nich nepodporuje web requesty :-).

Výhod použití IoC/DI jsem pak také využil i v případě předávání servisních objektů do VM, většinou pak přes constructor injection.

Role Controlleru

Jak jsem již zmínil, snažil jsem se odlehčit Controller, jeho úloha v mém pojetí se tedy redukovala na tyto základní operace:

  • vytvoření ViewModelu
  • nabindování dat – většinou dat z requestu
  • zavolání vhodné metody na ViewModelu – analogicky k desktop aplikaci, vyvolání vhodné události
  • propojení ViewModelu na příslušné View

Z předchozí věty by se mohlo zdát, že ViewModel je svázán s konkrétním View a nemůže bez něj koexistovat, což ovšem není pravda a VM je nezávislou jednotkou a může k prezentaci dat využívat několik různých View.

Když jsem se zmínil o vytvoření ViewModelu, je třeba říct, že ani toto nemusí být tak úplně úlohou Controlleru, stejně tak jako nabindování dat, neboť toto může zajistit vhodně napsaný a upravený ModelBinder.

Tím se tedy dostávám k odpovědi na Augiho otázku pod minulým příspěvkem, jakým způsobem řeším bindování dat. Určitě nepoužívám generický ViewModel, ten by bylo možné použít možná u jednoduchých CRUD stránek. Zároveň určitě nepředávám do ViewModelu kolekci FormCollection, neboť bych ViewModel příliš svázal s prostředím a já se snažil jej mít co nejvíce nezávislý – i ono vložení TempDataDictionary je řešeno přes interface poskytující přístup k dictionary.

Tudíž odpověď je následující. U stavových stránek je za vytvoření správného ViewModelu v akci zodpovědná daná akce, která též vybírá View. Vytvoření se pak děje přes resolving instance IoC/DI containeru. V případě, že se jedná o request mající za úkol vykonat příkaz, potom jsou dvě možnosti. Nechat ViewModel vytvořit upraveným ModelBinderem a bindování dat ponechat v jeho režii, případně postupovat obdobně jako ve stavové akci a binding ponechat na metodě UpdateModel/TryUpdateModel.

Osobně jsem volil spíše druhý přístup, ale to mělo spíše jiný důvod, ke kterému se přiznám a rovnou se i vymluvím :-) Funkce ModelBinderu mi byla delší dobu malou neznámou a převod dat jsem řešil spíše přes ValueProvidery, toto řešení však ve verzi 1.0 bylo zavrženo a já se nakonec stejně musel použití ModelBinderu naučit. A samozřejmě i ověřit, že tento přístup s použitím upraveného MVVM je stále funkční a vytvoření VM je možné i v ModelBinderu.

Těsně před koncem?

Tím se pomaličku dostávám ke konci tohoto příspěvku. Vím, že jsem neodpověděl na všechny otázky, které vyvstaly, ale toto určitě není poslední příspěvek na toto téma. Jistý podnět mám i ke komentářům v předchozím článku, které nabízeli možné řešení plugin modelu v asp.net MVC aplikaci. Toto řešení se mi nezdá úplně optimální a mám k němu jisté výhrady a určitě by se dala nalézt lepší implementace při použítí mnou navrženého modelu.

Diskuze

Jak jsem též v komentáři uvedl, budu rád, pokud se ozvete, ať už pomocí komentáře pod článkem nebo přes kontaktní stránku a projevíte zájem o možnou osobní diskuzi nad tvorbou MVC aplikací (nemusí se jednat jen o asp.net MVC implementaci). Každý podnět uvítám a určitě se najde prostor k zorganizování takové diskuze, kde může dojít ke sdělení a výměně poznatků.

analysis-sw-architecture
Posted by: Jarda Jirava
Last revised: 07 Apr, 2009 02:31 PM History

Comments

pabeblime
pabeblime
05 Feb, 2012 01:49 AM

iCbeihgssej free games for ipod
CrreercdePumd

Esserorge
Esserorge
02 Feb, 2012 01:32 PM

Hello, i read your site, this a best site from me, thanks!

18 Feb, 2011 09:42 PM @ version 0

Pekné pekné. Dokážu si predstavit, jak to pekne funguje...jen jedna vec mi není jasná (že by námet na další clánek? ;)) a to ta, jak vypadá ViewModel. Pro úcely zobrazení mi prijde velmi vhodné, aby dával k dispozici properties, které pujdou jednoduše precíst ve view. Pri vykonání príkazu je pak potreba totéž, abysme mohli použít DefaultModelBinder (nebo používáš nejaký vlastní?). Obsahuje tedy Tvuj ViewModel properties, které v getterech a setterech pouze delegují volání na Modely? Možná by to šlo udelat trošku jinak - gettery nechat, ale místo setteru udelat metodu pro update, které by se jako parametr predal nástupce IValueProvidera - IDictionary<string, ValueProviderResult>. No, možná by se mu mohl predat dokonce celý ControllerContext...

18 Feb, 2011 09:42 PM @ version 0

Jinak o diskuzi bych urcite zájem mel. Ted jsem práve v takové fázi, že ješte neportuji z Preview5 ani Bety na Release verzi, protože bych chtel behem portu udelat takový refactor, aby výsledný kód používal "best-practices". Takže ted jen víceméne sbírám informace...

18 Feb, 2011 09:42 PM @ version 0

Hned vcera když jsem dopisoval tento clánek mi bylo jasné, že bude muset mít pokracování a to práve ve vysvetlení toho, jak vlastne vypadá samotný ViewModel a jak se s ním pracuje ve View. Takže místo fotbálku budu sepisovat a možná dojde i na ty zdrojáky (to neslibuju).

18 Feb, 2011 09:42 PM @ version 0

No ne, to zas netreba hrotit - klidne bež na fotbálek, dokud je hezky :)

09 Jun, 2009 06:25 PM @ version 0

Osobne bych doporucil zustat u názvu MVC. MVVM je návrhový vzor, který se v poslední dobe objevil pro WPF a je možné ho použít pouze na platforme s dobre udelaným databindingem, což web asi nikdy nebude. Mám pro to dva duvody:

  1. V tvém rešení se stále používá controller, takže korektní by bylo používat "MVCVM". Na platforme s dobrým databindingem controller odpadá, je nahrazen reakcemi ViewModelu na zmenu dat (databindingem) a dokonce i príkazy jsou vkládané do UI pres databinding (napr. navázání tlacítka na ICommand poskytnutý jako property ViewModelu).

  2. To písmenko "M" v MVC nemusí nutne znamenat doménový model. V tvém prístupu je práve ViewModel modelem pro view, tudíž zastupuje ono "M". At už ho nazveme jakkoliv, stále se jedná o MVC, nikoliv o MVVM.

Rozlišit doménový model (DataModel, DomainModel...) a prezentacní model (PresentationModel, DTO, Screen Bound Objects, ViewModel) je samozrejme dobrá vec, která zprehlednuje architekturu a separuje View od možnosti cokoliv páchat s doménovým modelem. Možná by v tomto prípade bylo lepší místo termínu ViewModel použít jiný název, aby nás to nesvádelo k analogiím s MVVM. Osobne bych se primlouval k PresentationModel, to prosazoval už ten fousatej chlap: http://martinfowler.com/eaaDev/PresentationModel.html

Your Comments

Used for your gravatar. Not required. Will not be public.
Posting code? Indent it by four spaces to make it look nice. Learn more about Markdown.

Preview