Jarda Jirava

Vývojář a architekt řešení postavených na technologii .net framework. Zabývám se jak vývojem webových aplikací za pomoci asp.net, tak také desktopových aplikací winform. Při návrhu řešení a samotném vývoji pak využívám dlouholetých zkušeností se zpracováním obchodní logiky a pravidel aplikací získaných z vývoje komerčních aplikací pro finanční a bankovní instituce.

Microsoft MVP

Microsoft MVP - Client App dev

Poslední příspěvky

21
Oct

Testování aplikací - panelová diskuze – materiály

Minulý týden ve čtvrtek proběhla panelová diskuze na téma testování aplikací. K této diskuzi jsem si přizval známé české vývojáře nejen se světa .NET, ale také Ruby, Python, Javy, Flexu a PHP.

Před celkem asi 80 lidmi v sále tak po mém krátkém představení tématu předstoupil Karel Minařík, který začal sérii krátkých přednášek o způsobech testování a různých možnostech, jak na testy aplikací psané vývojářem pohlížet. Po tomto skvělém výkopu se představil Michael Juřek zastupující .NET komunitu a Microsoft, představil nástroje z prostředí Visual Studia, které je možné pro testy využít, a nastínil také blízkou budoucnost.

Poté již následovala přednáška Borka Bernarda, který se na testování aplikací podíval ze strany Flexu a taktéž nastínil, jak snadné nebo složité může být testovat komponenty a/nebo interakci vizuálního prostředí.

Pohled nejen ze světa Javy, ale hlavně z prostředí enterprise aplikací a možností testování v těchto komplexních aplikacích představil Roman “Dagi” Pichlík. Pohovořil na téma virtualizace služeb pro integrační testování.

Poslední slovo přednáškové části si vzal na starost Honza Král a jeho shrnutí nemělo chybu, alespoń z mého pohledu. Myslím, že spoustě přítomných vyjasnil pojmy, se kterými se vývojář, který se rozhodne pro testování svých aplikací, setká.

Tím vše neskončilo, spíše teprve začalo. Všichni pozvaní hosté se ujali zodpovídání otázek. Určitě nedokážu interpretovat veškeré otázky a odpovědi, ale to snad ani nemusím, nebot téměř vše zachytil přítomný mikrofon a vy si tak můžete průběh diskuze poslechnout.

A jelikož se někteří jistě ptáte i po materiálech, které byly prezentovány, zde jsou:

  • Karel Minařík – html (531kB) (otevírejte nejlépe ve Firefoxu)
  • Roman “Dagi” Pichlík – pptx (413kB)
  • zvukový záznam přednášek (bude doplněn co nejdříve)
  • zvukový záznam diskuze (bude doplněn co nejdříve)

Doufám, že se tato panelová diskuze líbila, a budu se těšit na setkání u některé další přednášky.

Update 21.10. 22:14

Zapoměl jsem zmínit shrnutí, které sepsal Dagi na svém blogu, takže tady to je: testování aplikací – panelová diskuze.

Zároveň mám výjádření od Borka Bernarda, který slajdy ke své části přednášky chce uveřejnit u sebe i s potřebným popisem.

Publikováno pod: analysis/sw architecture
5
Jun

asp.net MVC – když Model není Model

Tak jsem si ve svém shrnutí diskuze o asp.net MVC pěkně naběhl, když jsem vyřkl následující větu: “Model představuje data k zobrazení”.

Při tomto výroku jsem myslel především na skutečnost, že je častou chybou představit si Model jako databázi a jako takový je potom znám ve View. V takovém případě pak má View přímý přístup k databázi, což se mi nelíbí a určitě bych toto nedělal a ani nemohu doporučit (na čemž jsme se v diskuzi shodli). Už jsem však nemyslel na pokročilé vývojáře, kteří dle definic znají pod pojmem Model aplikaci/služby a přitom dokáží svůj model připravit k využití v Controlleru tak i k zobrazení bez následků ve View.

Poté, co jsem si vše nechal uležet mi vše docvaklo a zjistil jsem, že pojmenování nebylo z mé strany úplně vhodné. Tudíž je na čase se omluvit.

Takže Model, tak jak je pojímán v definici MVC není skutečně tím Modelem, který jsem vyřkl v oné větě. Pro toto mé označení by se více hodilo označení ViewModel, tak jak je pojímáno v jedné z implementací MVC a to MVVM.

Pro příště se tedy budu držet terminologie, že View zná svůj ViewModel, to samé pak platí pro komunikaci směrem od Controlleru k ViewModelu. Pouze ViewModel pak ví o Modelu. A nesmím ještě zapomenout na propojení z Controlleru na View.

To jak se právě uvedeného propojení dá využít v psaní asp.net MVC aplikace se pokusím naznačit v příštím článku. A možná se mi tak podaří ukázat, že ono pověstné přidání komentářů může znamenat změnu jen jednoho místa v … a poté změnu samotných View, tak jako by to bylo nutné udělat ve WebForms.

4
Jun

asp.net MVC Best practices – shrnutí

Ve středu večer proběhla diskuze v půlkulatém kruhu na téma asp.net MVC Best practices, kterou se mi povedlo uspořádat v prostorách Microsoftu a pozvat na ni zástupce různých technologií.

Něco málo k organizaci

Dřív než se dostanu k samotnému shrnutí získaných informací, dovolím si uvést několik málo informací k organizaci a průběhu samotné diskuze. Tato totiž dopadla nad očekávání dobře a dle reakcí účastníků to není jen můj osobní pocit, za což jsem velice rád a musím především zúčastněným poděkovat. Maličko jsem se totiž obával, když jsem sezval zástupce různých technologií, aby diskuze nesklouzla k flame-ování, ve výsledku z toho byla však konstruktivní debata trvající téměř 3 hodiny, a po ní ještě flám-ování. Je vidět, že pokud se sejdou lidé, kteří mají spoustu znalostí a zkušeností, dokáží být nad věcí a přistoupit k diskuzi konstutivně. Zároveň se pak velice snadno dokáží přizpůsobit a orientovat se i v jiné technologii, jelikož se zde řeší stejné úlohy, pomocí stejných vzorů, jen s odlišnou implementací.

Pro příště mám však jedno ponaučení, zúčastnění by se měli na takovéto diskuzi osobně představit. Přestože jsem připravil vizitky velikosti A4 se jménem a zastoupenou technologií, nebylo to dostačující. A tak David Grudl (Nette PHP) zjistil až někdy kolem půlnoci, že se zúčastnil také Michal Bláha (.NET WebForms). Zajímavá chvilka taktéž nastala při diskuzi, kdy Vlasta Vávrů (Java, PHP) se podivil nad komplexností deploymentu popisovaného Honzou Králem (Django). Zajímavé postřehy pak měli také Karel Minařík (Rails), Borek Bernard (Flex) a Daniel Kolman (.NET MVC). Musím též poděkovat Aleši Roubíčkovi a Michalu Augustýnovi za pomoc a podporu při organizaci. Samozřejmě patří dík i ostatním, kteří se zúčastnili a zapojili se do diskuze.

Ostatním, kteří měli zájem se diskuze zúčastnit, nebo mají zájem dozvědět se závěry z diskuze mám potěšující zprávu, v dohledné době proběhne přednáška na téma asp.net MVC, kde budou prezentovány závěry z uskutečněné diskuze vzešlé.

Pro nedočkavce – shrnutí MVC Best practices

Myslím, že velice detailní shrnutí již sepsal Borek Bernard v ohlédnutí z diskuze o MVC a svůj pohled na, asi jedinou flame diskuze, pak Tomáš Herzeg o rozdílech mezi WebForms a asp.net MVC.

Přidám tedy jen svůj pohled, který doufejme doplní výše uvedené články. Co jsem si odnesl z diskuze já a co mě velice potěšilo, že jsem se vesměs se svými předchozími články popisujícími použití MVVM v asp.net MVC celkem trefil do toho, jak uvažují i ostatní o přístupu k implementaci MVC principu pro webové aplikace. Právě David Grudl popisoval velice podobný scénář, který použil v Nette pomocí “plniče” Presenteru, jako jsem uvedl v článku jak vypadá ViewModel v asp.net MVC.

V hlavní roli Model

Co je třeba si uvědomit při využití jakékoliv implementace MVC je role Modelu. Jak zmínili téměř všichni přítomní, především u začínajících vývojářů je Model považován za databázi, což určitě není. Model představuje data, která jsou připravena k zobrazení. Na co určitě zapomeňte je předávání DataContextu do View, pokud chcete využít třeba LINQ2SQL nebo Entity Framework. Maličko lepší službu už uděláte v případě, že předáte jen vygenerované datové objekty, na mnoha malých webech to bude dostačující. Jestliže však uvažujete o něčem větším, vytvořte si vhodný view model, který bude respektovat potřeby pro zobrazování v aplikaci, nikoliv potřeby relační databáze. Do modelu se pak nebojte zahrnout i podpůrné vlastnoti vhodné při zobrazování dat.

Jednoduchý Controller

Citovat Davida si dovolil již Borek, já mohu jen souhlasit. Controller by měl být co nejjednodušší. Měl by se postarat jen o výběr vhodného ViewModelu (Presenteru), zvalidovat vstupní data a vybrat šablonu (View), která provede zobrazení dat. Případně se samozřejmě postará o přesměrování na jinou akci, což je jen o tom, že vybere jiný ViewModel a jinou šablonu, která se zobrazí.

Pasivní View

Přesně tak, nesnažte se do View vkládat složitější logiku než je jen vypsání dat na potřebná místa. Šablony v asp.net MVC mohou svádět k tomu je vytvořit aktivní a manipulovat zde s Modelem, obzvláště pak v případě, že si do View předáme potřebné objekty typu DataContext. Dobrým řešením by mohlo být použití takového ViewEngine, který dovoluje pouze deklarativní zápis, případně komponentové poskládání stránky – obdobně jako bylo představováno frameworkem Django.

Nenecháme si to pro sebe?!

Samozřejmě je toho víc a výše zmíněné je jen to hlavní co mi utkvělo v paměti. O další informace se budu chtít s vámi podělit. Kdy to konkrétně bude ještě nevím, ale určitě sledujte vypisované akce. A není to vše, jak jsem se již zmiňoval v úvodu, snad všichni pozvaní vývojáři byli uspořádáním takovéto akce nadšeni a rádi se zúčastní obdobných diskuzí. A jelikož se více jak půl hodinu taktéž diskutovalo o testování aplikací, předběžně jsme se domluvili na tomto tématu. Představa je formou panelové diskuze, tudíž pokud bude mít někdo zájem, určitě se bude moci zúčastnit.

Budu se tedy těšit na brzkou viděnou se všemi zájemci o vývoj pomocí asp.net MVC.

24
May

asp.net MVC Best practices - pozvánka

Také se zajímáte o vývoj za pomoci MVC vzoru. Máte k tomuto přístupu k vývoji prezentační vrstvy nějaké otázky nebo si naopak myslíte, že byste mohli přispět svojí radou nebo zkušeností?

Potom budu rád, pokud se registrujete na akci asp.net MVC Best practices a svoji motivaci připíšete do poznámky.

Popis akce

K diskuzi jsou přizváni též vývojáři zastupující technologie nespadající pod .net, avšak jejich vývoj je též založen na principu návrhového vzoru MVC. Cílem diskuze je najít vhodné postupy při vývoji webových aplikací, které využívají principu MVC a odvozených návrhových vzorů tak, aby se dosáhlo efektivního vývoje aplikace, která dovoluje snadné úpravy a rozšiřitelnost. Závěry, rady a postřehy z této diskuze by měli sloužit nejen začátečníkům v orientaci při vývoji webových aplikací. Diskutující se též seznámí s implementací MVC v jiných technologiích než je asp.net. Důležitým předpokladem pro účast je tak znalost vývoje pomocí MVC návrhového vzoru, případně jeho odvozenin.

Přizvaní hosté

David Grudl, Jan Král, Vlastimil Vávrů, Michal Bláha, Borek Bernard, Aleš Roubíček, Michal Augustýn, Karel Minařík a další

10
May

.NET RIA Services – úvod

V okamžiku, kdy jsem začal přemýšlet nad obsahem přednášky o .NET RIA Services jsem měl velice smíšené pocity. Jedním z nich byl i ten, že se jedná o technologii, která je teprve v plenkách, tedy v CTP verzi, a přitom by si po jejím představení jistě spoustu vývojářů přálo, aby již byla dostupná pro produkční prostředí.

Tím dalším pocitem pak byl ten, že na jedné přednášce se nedá předvést vše, co .NET RIA Services nabízejí vývojáři při vývoji data entry aplikací. To se však dá vyřešit celkem jednoduše, jedna varianta je uspořádat přednášku druhou, nebo o této technologii – projektu nebo šabloně, záleží, jak se na to podíváme – napíši několik článků. Jak je jasné ze čtení těchto písmenek, rozhodl jsem se pro druhou variantu, kdy se pokusím představit .NET RIA Services v sérii článků. Mojí snahou bude věnovat se nejen samotné technologii, ale i jejímu zasazení do návrhu projektu.

n-vrstvá architektura aplikací

Když se podíváme na spoustu vyvíjených aplikací, které se snaží využívat objektového návrhu a výhod n-vrstvých aplikací, zjistíme, a to především u začínajících vývojářů, že se jedná vlastně jen o dvouvrstvé aplikace. Taková aplikace se vyznačuje tím, že hned v obsluze událostí uživatelského rozhraní se přistupuje k datové vrstvě a zároveň se v této prezentační vrstvě řeší i aplikační logika aplikací.

Jak jistě každý hned vidí, tento stav není vůbec ideální a přináší s sebou spoustu kódu, který je při rozšiřování aplikace třeba refaktorovat, v horším případě pak kód duplikovat.

Umístění servisní vrstvy s aplikační logikou

S příchodem RIA (Rich Internet Application), které je možné vyvíjet za pomoci .net frameworku však vzniká další požadavek, který je nutné řešit a to je nejen oddělení servisní vrstvy, ale zpřístupnění této vrstvy jako endpoint pro přístup z prezentační vrstvy přes internet. Samozřejmě nyní můžete navrhnout, že toto zpřístupnění je možné vytvořit například pomocí WCF, nebo jen vytvořením web services v již existující web aplikaci. Ti kteří vyvíjejí nad poslední verzí .net frameworku pak budou znát ADO.NET Data Services, jakožto přístupový bod k jejich datům.

Jak však vidíte, není to stále to pravé, vždy je potřeba udělat ještě jeden krok, který naší RIA aplikaci poskytne data a zapojí servisní vrstvu s aplikační logikou do celé aplikace.

RIA aplikace

Nyní si dovolím krátkou odbočku a pokusím se přiblížit ze svého pohledu RIA aplikace. Jsou to aplikace, jejichž prezentační vrstvu stále tvoří tenký klient. Tento klient však již není úplně “hloupý” a nabízí uživateli komfort blížící se tlustým nebo smart klientům. Tyto aplikace se nemusí instalovat, nebo k jejich instalaci je nutný jen minimální instalační balíček (framework), který využívají.

RIA aplikace můžeme využít všude tam, kde potřebujeme zajišťovat vstup dat od uživatelů tzv. data entry aplikace, kde právě komfort při zadávání dat zpříjemňuje uživateli jeho práci. Stejně tak do těchto aplikací můžeme zařadit LOB – Line Of Business – aplikace.

Které technologie můžeme očekávat ve spojení s RIA aplikacemi? Pokud zůstanu jen u .net aplikací, tak sem patří asp.net webové aplikace s bohatým využitím AJAXu, dalším silným zástupcem můžeme považovat Silverlight aplikace. Obecně by se pak dalo říci, že se jedná o takové aplikace, které umožňují interaktivitu aplikace s koncovým uživatelem na klientské straně a data jsou poskytována vzdáleně přes internet.

.NET RIA Services

Po přečtení předchozích odstavců mohu konečně přistoupit k popisu toho, k čemu byly vytvořeny .NET RIA Services, a které možnosti a usnadnění nám nabízejí při vývoji.

Jak již samotný název napovídá, jedná se o servisní vrstvu, která nám usnadňuje zapouzdření aplikační logiky na serverové straně a vytvoření n-vrstvé aplikace. Tato servisní vrstva pak bude obsahovat nejen aplikační logiku, ale též validaci vstupních dat, přístup a využití datové vrstvy k získávání dat, jejich uložení a modifikaci. Ale také může a nejspíše bude řešit authentikaci a řízení přístupu pomocí rolí.

Kompletní balíček však není jen o vytvoření servisní vrstvy, ale je zde zahrnut i balík komponent, které můžeme využít při tvorbě Silverlight aplikace, stejně tak zde najdeme prvky, které využijeme při vývoji asp.net webform aplikace. V neposlední řadě je to potom velice důležitý samotný projekt, který obsahuje připravené šablony a build akce, které se starají o generování kódu pro klientskou stranu – tak, aby byla zajištěna její bohatost.

DomainService

Na asi nejdůležitější část .NET RIA Services a to třídu DomainService a její potomky se podívám podrobněji v příští části tohoto seriálu.

10
Apr

Jak vypadá ViewModel v asp.net MVC

Při psaní úvodního článku, ve kterém jsem se věnoval postřehům při vývoji asp.net MVC aplikace jsem si ani nepomyslel, že vydám i druhý článek, který bude popisovat Model-View-ViewModel upravený vzor pro takovou aplikaci. A už vůbec to, že bych se rozhodl ke psaní článku třetího o tom, jak vlastně taková třída ViewModel v mém podání vypadá.

Okolnosti tomu však chtěli a tak jsem zde s dalším pokračováním na rozpracované téma. Tentokrát se pokusím osvětlit, jak vypadá třída ViewModel v mém podání zasazená do asp.net MVC aplikace.

ViewModel

Jak jsem již popsal, třída ViewModel zprostředkovává data a komunikuje se sevisními objekty aplikace a zároveň poskytuje data aplikace pro View. Zároveň pak upravuje tato data, aby s nimi ve View byla snazší práce a View mohlo být velice jednoduché a nevykonávalo žádnou logiku. Ještě neopomenu zmínit jednu věc, že k vytvoření konkrétní instance ViewModel používám IoC/DI container, tudíž servisní objekty jsou injektovány a o jejich existenci tak nemusí mít Controller ponětí.

Zkusím projít popisem tak, jak většinou prochází takový normální požadavek na získání informací a jejich editaci a to pro jeden datový objekt.

Akce Controlleru vypadá nějak takto

public ActionResult Update(int id) {
    var model = Container.Resolve<ProductDetailViewModel>();
    model.Load(id);
    return View(model);
}

Odpovídající třída ViewModelu pak nějak takto

public class ProductDetailViewModel {
    private readonly IProductService _productService;
    public ProductDetailViewModel(IProductService productService) {
        _productService = productService;
    }
    
    public void Load(int id) {
        Product = _productService.Get(id);
    }
    
    public ProductEntity Product {
        get;
        private set;
    }
    
    public bool IsProductLoaded {
        get { return Product != null; }
    }
}

Pro jednoduchost jsem momentálně odstranil další potřebné servisní objekty o kterých jsem se zmiňoval již dříve. Například providera na TempDataDictionary. Zároveň je vidět, že ProductDetailViewModel obsahuje i další vlastnosti, které jsou použity ve View a odpadá nám tak nutnost testování a rozhodování se na úrovni View.

Nyní přejdu k akci, kdy uživatel žádá o aktualizaci takto poskytnutého záznamu, který zaktualizoval.

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Update(int id, FormCollection formCollection) {
    var model = Container.Resolve<ProductDetailViewModel>();
    model.Load(id);
    if (TryUpdateModel(model.Product, "Product", null, new string[] {"Id"})) {
        if (ModelState.IsValid) {
            model.Save();
            return RedirectToAction("Get", new { id = id});
        }
    }
    return View(model);
}

Jak je vidět, téměř nic se nezměnilo, jen přibyla metoda Save() na našem ViewModelu, která zpropaguje požadavek na servisní vrstvu.  Celé by to šlo samozřejmě ještě upravit tak, že by metoda Save vracela výjimku v případě, že by se nepodařilo záznam uložit a došlo by k znovupožadavku na editaci. Je však na samotné logice aplikace, jak se s takovou chybou vypořádá a zda nabídne uživateli opět možnost editace a odstranění problému, nebo jej přesune na jinou stránku.

Důležitou součástí celého procesu se tak stává ModelBinder, který může zároveň provést validaci vstupních dat oproti business pravidlům.

Spolupráce s ModelBinder

Výše uvedené je celkem pěkný postup, ale stále zde je ještě spousta kroků, které se mohou přesunout, abychom se mohli soustředit jen na samotné akce a pokud možno se co nejvíce přiblížili k pouhému vyvolávání metod na ViewModelem, tak jako se vyvolávají události ve WPF aplikaci při použití vzoru MVVM.

K tomu však potřebujeme maličko lepší spolupráci ModelBinderu, než která nám je nabízena prostřednictvím výchozího DefaultModelBinderu. Hlavní úlohou námi definovaného ModelBinderu je vytvoření instance ViewModelu a validace vstupních dat. Samozřejmě si zde můžeme připsat i další logiku bindování na data.

Takový ModelBinder pro náš ViewModel k detailu produktů může vypadat následovně:

public class ProductDetailViewModelBinder: DefaultModelBinder {
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
        bindingContext.Model = Container.Resolve<ProductDetailViewModel>();
        var id = int.Parse(bindingContext.ValueProvider["Product.Id"].AttemptedValue);
        bindingContext.Model.Load(id);
        return base.BindModel(controllerContext, bindingContext);
    }
}
Samozřejmě opět odhlížím od kontrol, které by bylo potřeba doplnit. Zároveň by bylo vhodné doplnit validaci nabindovaných dat oproti business pravidlům, což je pro zjednodušení vynecháno.

Akce v příslušném Controlleru se nám tedy rázem zjednoduší a její implementace bude následující:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Update([Bind]ProductDetailViewModel model) {
    if (ModelState.IsValid) {
        model.Save();
        return RedirectToAction("Get", new { id = model.Product.Id});
    }
    return View(model);
}

Což už je myslím akceptovatelný stav. Samozřejmě bude záležet na okolnostech a celkovém chápání aplikace. Neboť tento případ skrývá před vývojářem samotné naplnění modelu daty třídy Product, což je na druhou stranu obdobný případ, jaký nastává ve WPF aplikaci, kdy nedochází ke ztrátě stavu a pracujeme již s existující instancí naplněnou daty.

Před cílovou rovinkou

Už je mi celkem jasné, že jsem se nedostal ani před cílovou rovinku a vyvstaly další otázky. Třeba jak řeším právě předávání dočasných dat v TempDataDictionary a jak dochází k jejímu injektování do ViewModelu. Takže u tří článků určitě nezůstane. V příštím článku se tak pokusím soustředit se na tuto oblast a případně zodpovědět vložené dotazy.

7
Apr

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ů.

6
Apr

Postřehy k asp.net MVC

Na chvíli se přesunu od mých oblíbenějších témat k tématu, které se v poslední době často přetřásá, a tím je použití MVC při tvorbě asp.net aplikací. Jelikož vývojáři dostali před poměrně nedávnou dobou k dispozici implementaci tohoto vzoru pro .net framework od MS, a to v jeho první verzi, určitě neuškodí, pokud se na něj podívám z praktického hlediska.

MVC model vývoje jsem při vývoji webové aplikace, nebo spíše prezentace použil již před nějakou dobou, kdy jsem nasazoval svoji verzi blogu. Postupně jsem pak přešel na použití Preview verzí asp.net MVC a následně jsem Beta verzi použil při komerčním vývoji jedné aplikace.

Již v době Preview verzí jsem však maličko zápasil s tím, jakou úlohu hraje v tomto přístupu Controller, který tvoří onu aktivní část. A právě Controller se při větších aplikacích poměrně rozrůstá, což se mi přestalo po nějaké době líbit a tak jsem se snažil najít způsob, jak mu, takříkajíc, odlehčit.

Související otázky

Zároveň s tím jsem začal řešit i několik dalších navazujících otázek. Na některé z nich jsem prozatím nenalezl odpověď, jiné se mi povedlo vyřešit s tím, jak byly přidávány nové vlastnosti.

Jednoduchý systém pluginů

Mezi jedny z hlavních nedostatků tohoto přístupu vidím horší možnosti implementací pluginů. Neříkám, že je to nemožné, a existuje několik pokusů, např. SubControllery, nebo metoda RenderAction. Stále to však není tak elegantní, jak to je možné například v klasických webforms.

CRUD vs Content page

Jinou úlohou pak může být případ, kdy potřebujete jednoduchou obsluhu pro správu modelu, pokud je Controller poměrně jednoduchý a využívá jen jeden, případně několik málo servisních objektů poskytujících data - záměrně nemluvím o repository, která se mi v ukázkách, které jsou v hojné míře k vidění, nelíbí. Přeci jen je občas potřeba s předanými daty ještě něco málo udělat, transformovat je, a až následně uložit. Myslím si, že tuto práci by však neměl vykonávat Controller jako takový, ale právě nějaká servisní vrstva dříve, než se data dostanou k repository objektu – v takovém případě jde vše celkem hladce a jednoduše. K opačnému pólu složitosti a nabubřelosti se však dostávám v okamžiku, kdy je potřeba vytvořit maličko složitější Content page, která se skládá z více datových objektů, které poskytují různé servisní objekty.

Analogie s desktop aplikacemi

V průběhu vývoje jsem se tak stále více dostával k otázkám, zda by nešlo využít dosavadních znalostí z vývoje desktopových aplikací a vhodnou formou je použít při vývoji asp.net MVC aplikace.

Jelikož se v poslední době zabývám především vývojem WPF aplikací a zde se poměrně dobře ujal vzor MVVM (Model – View – ViewModel) pokusil jsem se jít touto cestou.

Malé odbočení na vzor MVVM

Jedná se o poměrně mladý návrhový vzor, který využívá výhod WPF a to zejména při bindingu. Spadá pak do stejné “škatulky” vzorů, které vycházejí z principu MVC, obdobně jako třeba vzor MVP (Model View Presenter). ViewModel pak, zjednodušeně řečeno, poskytuje jak data Modelu samotná, tak i připravuje další data, která jsou využívána View pro prezentaci. View je v takovém případě pasivní, čehož je zejména u WPF dobře skloubeno s reakcí na události vyvolávana v Modelu, případně právě ViewModelu.

Jak jsem již uvedl, mé rozhodnutí bylo učiněno v souvislosti s analogií na desktopové aplikace, kde je přeci jen bohatší objektový model, proto si určitě nemyslím, že mé řešení může být za všech okolností to pravé a správné – ani nechci tvrdit, že je to přesně MVVM implementace vzoru, přeci jen je zasazen do jiného modelu, ale ona analogie zde byla.

Řešení pomocí MVVM

Postupně jsem se přes problémy dostal k nástinu možného řešení. Netvrdím, že bude všespásné, mě se však v současné chvíli líbí. Alespoň co se týká použití v asp.net MVC, samotné asp.net nebo-li webforms neberu v tuto chvíli v potaz, neboť mají jiný přístup a mnoho věcí se zde dá řešit jiným způsobem.

Mým cílem bylo:

  • odlehčit samotnému Controlleru
  • zároveň jsem nechtěl řešit logiku aplikace ve View, což je v mnohých ukázkách poměrně časté. Jedná se především o případy, kdy se testuje zda hodnota v modelu je právě taková, potom vypiš toto a jinak tamto
  • dalším požadavkem byla silná typovost ve View, to znamená žádné ViewData[klíč]
  • … další podružné věci na které si momentálně nevzpomenu

Jak tedy mé řešení vypadá:

Každé View má přiřazenu svoji třídu ViewModel, která je instancována až v dané akci Controlleru. ViewModel si následně obhospodařuje práci se servisními objekty, které mu poskytují data - Model. Odlišností od původního návrhu pak je, že ViewModel nereaguje přes události na View, ale tyto reakce jsou mu vnuknuty od Controlleru voláním metod.

Tím se mi podařilo docela vhodně eliminovat množství kódu v samotné akci Controlleru. Zároveň je tento ViewModel předán jako model do View, čímž je zajištěna silná typovost tohoto View přes vlastnost Model. Uvnitř třídy ViewModel je potom řešena logika pro zobrazování – nikoliv zobrazování samotné. Je třeba zde ještě zmínit, že ViewModel může poskytovat jako svoji vlastnost přístup k podřízeným ViewModelům, které jsou použity při konstrukci View. Zejména se jedná o případy, kdy je třeba předat data modelu do UserControlu.

Tento první nástřel možného řešení byl v první fázy dostačující, samozřejmě přišla i jistá úskalí, ale o těch zase příště.

8
May

Názor k použití objektů podle Ronnieho

Minulý týden jsem si přečetl příspěvek Ronnieho "Proč používat objekty - pokračování" a nedalo mi to, abych nereagoval v komentářích. Přeci jen mám trochu jiný pohled na danou problematiku vycházející z mých zkušenosti a dosavadních znalostí.

Pod článkem se rozjela fajn debata, ze které jsem poznal pohled i jiných vývojářů na používání objektů. Pokud jsem správně pochopil jejich výroky, nelíbí se jim možnost používat protected viditelnost u objektů. Konzistence a zapouzdření je pro objekty požadováno, avšak můj názor je takový, že tyto požadavky nejsou porušeny tím, že objekt zpřístupní svůj rozšířený interface svým potomkům. Znamená to, že objekt má vesměs dva (v případě .net tři) přístupné interface.

Public interface

První interface je tvořen public viditelností vlastností a metod. Tento interface je přístupný všem okolním objektům, které chtějí s naší instancí třídy komunikovat.

Rozšířený interface za pomoci protected viditelnosti

Public interface však není jediný, který může architekt zpřístupnit. Dalším a neméně důležitým je i tento interface - byť se to někomu nemusí zdát. Samozřejmě v tomto případě hodně záleží na samotném tvůrci, aby zajistil, že tento interface neporuší konzistenci objektu. To znamená, zpřístupnil pouze takový interface, který konzistenci neporuší. Nemusí se však jednat o public interface, ale pouze takový, který uvidí třídní potomci. V .net frameworku, a vesměs asi v každém propracovaném a dobře navrženém frameworku, nalezneme spoustu tříd, který zpřístupňují svůj rozšířený interface svým potomkům.

K čemu rozšířený interface použít

Napadá mě hned několik možností, při kterých návrhář třídy bude využívat tohoto rozšířeného interface k zajištění konzistence a přitom nedojde k porušení zapouzdření. Pro lepší názornost bude vhodnější uvést příklad takové třídy a jejího potomka.

Dejme tomu, že máme třídu Osoba, která má několik vlastností (atributů) a metod pro manipulaci s touto třídou. Jednou z veřejných metod je i metoda pro uložení objektu, tato metoda by měla zkontrolovat, zda je při splnění komplexnějších podmínek umožněno uložit objekt Osoba. Tyto podmínky platí i pro potomky.

V případě, že bych Osoba poskytovala pouze veřejný interface, musel by každý potomek zajistit kontrolu těchto podmínek, neboť ukládání je pro každého potomka malinko odlišné. Jak si ale v takovém případě může být autor třídy Osoba v tomto jistý? Řešení je v tomto případě zajištěno implementací návrhového vzoru Template method a tedy rozšířením interface pomocí protected metody. V takovém případě nebudeme public metodu k uložení označovat jako virtuální a po zkontrolování podmínek budeme volat protected metodu.

Taková třída tedy může vypadat následovně:

public abstract class Osoba {
public void Save() {
  if (podminkySplneny()) {
    saveInternal();
  } else {
    // zalogování informací a případné vyvolání výjimky
  }
}
protected abstract void saveInternal();
}

Tímto způsobem máme zajištěno, že objekt zůstane zapouzdřený a viditelný zůstane pouze požadovaný veřejný interface, neboť je celkem oprávněný požadavek, aby okolí nemohlo volat žádost o uložení objektu, bez kontroly splnění podmínek.

Je to jen jeden z mnoha případů, proč si myslím, že není nebezpečné používat také protected viditelnost u objektů. Ale jak jsem se již někde vyjadřoval, vše je to o konkrétním člověku. Pokud navrhnu veřejný interface špatně, může to být více nebezpečné než pokud správně navrhnu celou třídu i s rozšířeným interfacem.

Samozřejmě je to jen můj pohled na danou problematiku a nemusí to být ten nejlepší pohled, vychází však z mých současných znalostí a zkušeností. Určitě se nebudu bránit jakékoli konzultaci nebo připomínkám, neboť jenom tak je možné si utřídit poznatky a porovnat je s okolím.

10
Oct

Přístup k datům v .net 2.0

V předchozích příspěvcích, kde jsem v praktických ukázkách používal přístup k datům, jsem se zmiňoval o správné konfiguraci datového spojení. V této oblasti došlo k poměrně významným změnám oproti předchozím verzím .net frameworku.

Obecný přístup k datům

Pro obecný přístup k datům v předchozích verzích .net frameworku bylo nutné používat interface. Vznikla proto řada obecných řešení, jedním z nich byla i komplexní Enterprise library, případně několik dalších, většinou uvedených na CodeProject.com, která se snažila odstínit konkrétní implementaci a přístup k datové vrstvě a rozhodování ponechat na konfiguraci. Taková řešení byla dobrá, převážně založená na design pattern a to především konkrétně na vzoru Abstract factory, případně Factory metod. Jak už to však bývá, bylo zde jedno ale a to konkrétně generování výjimek. Tento neduh nespočíval ani tak v samotné implementaci, ale již v .net frameworku. A pokud se nepoužilo některé více komplexní řešení, které by jednotlivé výjimky zapouzdřilo, bylo velice obtížné takové Exception obecně odchytit. Jednotlivé výjimky totiž byly závislé na implementaci a tak nebylo možné odchytit např. DbException, která neexistovala, ale musela se zachytávat přímo SqlException, OleDbException atd.

S příchodem .net 2.0 se mnohé změnilo a to k dobrému.

Je dobře známé, že architektura .net frameworku využívá a je postavena na návrhových vzorech. A i pro práci s datovými zdroji bylo těchto vzorů hojně využito. Tím prvním, se kterým se setkáte je tak Provider pattern, který byl použit pro získání příslušné Abstract factory třídy, konkrétně potomka DbProviderFactory.

Získání DbProviderFactory

Abychom mohli přistupovat k datům v abstraktní rovině, musíme také používat abstraktní objekty jejichž konkrétní implementaci získáme až za běhu programu pomocí konfigurace. Tímto "tvůrcem", který nám zprostředkuje potřebné objekty je právě DbProviderFactory třída, kterou dostaneme při volání statické metody DbProviderFactories.GetFactory(). Po získání instance tak můžeme od této instance žádat voláním příslušných metod o vytvoření potomka pro DbConnection, DbCommand, DbCommandBuilder, DbDataAdapter nebo DbParameter.

Třída DbProviderFactories má pak ještě druhou statickou metodu nazvanou GetFactoryClasses() o které se ještě zmíním.

DbConnection, DbCommand a také DbException

Nejen zmíněné objekty tvoří abstrakci - Bridge - pro přístup k datům. I další objekty umožňující obecný přístup k datům, které je možné získat pomocí volání metod nad získanou instancí objektu DbProviderFactory, a jenž jsem zde neuvedl, jsou umístěny v System.Data.Common namespace. V tomto namespace jsou vedeny také výčty, jenž se mohou s těmito objekty použít, např. výčet datových typů.

Určitě bych neměl opomenout zmínit, že existuje také obecná databázová výjimka DbException a tak se přístup k datům může stát téměř univerzálním.

Dříve než se podívám na to, jak to celé tedy správně nakonfigurovat, neopomenu ještě jedno malé upozornění. Týká se parametrů, které se předávají do příkazů. Při vytváření parametrů se totiž nepřidává žádný prefix a tak tato operace je na vývojáři, který musí vědět, jaký symbol prefixu pro daný databázový stroj použít. Tímto se tak úroveň abstrakce malinko snižuje a musí být zvoleno vhodné řešení pro tvorbu parametrů.

Použití ConnectionStringSettings a .config soubor

K vytvoření správné instance DbProviderFactory potřebujeme znát provider name. Jeho přesný název je uveden u každé definice příslušné Factory v souboru machine.config a to v atributu invariant. Tento název poté použijeme v konfiguraci connectionstringu v atributu providerName, abychom jej následně předaly do volání metody GetFactory().

Sekce connectionStrings tak může vypadat následovně:


 

Samotný prováděcí kód pro získání příslušné faktory a vytvoření connection bude následující:

ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings["MainConn"];
DbProviderFactory factory = DbProviderFactories.GetFactory(settings.ProviderName);
using(DbConnection connection = factory.CreateConnection()) {
}

na základě výše uvedeného nastavení vytvoří spojení na SQL Server databázi

K jakým datovým zdrojům tedy máme přístup ihned po instalaci .net frameworku? Jsou to následující implementace:

  • Sql server - System.Data.SqlClient
  • Oracle - System.Data.OracleClient
  • OleDb - System.Data.OleDb
  • Odbc - System.Data.Odbc
  • Sql server CE - Microsoft.SqlServerCe.Client

jež jsou definovány v souboru machine.config. Samozřejmě nám nic nebrání v tom, napsat si vlastní Factory, která bude zprostředkovávat - tvořit - objekty pro přístup i k jiným databázím, např. FireBird nebo MySQL. Seznam všech dostupných implementací je k dispozici též za běhu programu po volání statické metody DbProviderFactories.GetFactoryClasses().

Měníme datové úložiště

Chceme-li tedy následně změnit datové úložiště, jediným zásehem tak může být záměna připojovacího řetězce a změna hodnoty v atributu providerName, která bude reflektovat nově používanou databázi a s tím nově používanou implementaci k tvorbě objektů zajišťujících přístup k danému datovému zdroji.

Na samotný závěr je třeba uvést, že z důvodu použití abstrakce se přichází o specifické možnosti, které jsou dostupné pro daný databázový stroj. I zde tedy platí, že každé plus má své minus.