Friday, 24 February 2012
Entity Framework 4.3. Code First - (nechutný) problém s TPC mapováním?
Update 25. 2.2011: Tak chyba potvrzena EF týmem. Jedná se skutečně o chybu, která je částečně popsána v known issues.
Diego B Vega : @Rene Stein: Thanks for reporting this and for the repro. What you describe seems to be a bug in TPC mapping that we are already aware of and that we are planning to fix in the upcoming EF 4.3.1. Please take a look at the list of known issues above for more information.
Jedná se tedy o chybu, kterou někdo zmínil i v komentářích. Zarážející ale je, že k chybě “chybí sloupec v databázi” se dostanete teprve tehdy, kdy vygenerujete databázi s jiným než požadovaným schématem, odchytnete výjimku při dotazování a podíváte se na popisek vnořené výjimky. V “known issues” EF by spíš podle mě mělo být - ve verzi 4.3 se vám ani nepodaří vygenerovat databázi s TPC mapováním dědičnosti a volání metody MapInheritedProperties při konfiguraci entit je jen zbytečná dekorace v kódu a cvičení v marnosti.
Mohl by prosím někdo ověřit, že jsem buď udělal nějakou triviální chybu při mapování, anebo potvrdit mé podezření, že je EF Code First v poslední verzi 4.3 natolik prolezlý chybami, že v něm nefunguje ani tento triviální scénář.
Problém se snažím reprodukovat na tomto kódu.
Mám třídy Base a Derived. Jejich role asi vysvětlovat nikomu nemusím.
Snažím se pro mapování třídy Derived do databáze použít v db kontextu strategii TPC – table per (concrete) class (metoda MapInheritedProperties).
Po spuštění se aplikace vytvoří databáze se dvěma tabulkami. Struktura databáze ale odpovídá TPT strategii pro mapování dědičnosti:
Tabulka Base má sloupce Id a BaseProperty, tabulka Derived Id a Note. Volání MapInheritedProperties je tedy zcela ignorováno.
Jak popisuju i v kódu, matoucí je to, že Entity Framework sice mapuje třídy do databáze podle TPT strategie, ale dotazy klade, jako kdyby v databázi byly třída Derived namapována TPC strategií.
Vygenerovaný SQL dotaz do tabulky Derived vypadá takto:
SELECT '0X0X' AS [C1], [Extent1].[id] AS [id], [Extent1].[BaseProperty] AS [BaseProperty], [Extent1].[Note] AS [Note] FROM [dbo].[Derived] AS [Extent1] Schizofrenní Entity Framework se beze všech skrupulí snaží dohledat v tabulce Derived sloupec BaseProperty (TPC mapování), což pochopitelně skončí výjimkou při vykonávání dotazu, protože se jiná část jeho vícečetné osobnosti složené z nespolupracujících spoluautorů EF rozhodla při generování databáze, že TPT je pro každého aplikačního vývojáře vždycky jasná volba.
A protože perverzních projevů EF se při pátečním večeru nelze nabažit, tak tady je skript pro založení databáze, který jsem vytáhl z podkladového ObjectContextu a který by měl mapovat podle TPC, což se ale nestane, protože je proti databázi spuštěn skript zcela jiný.
Výsledek Trace.WriteLine(((IObjectContextAdapter) context).ObjectContext.CreateDatabaseScript());
Projekt s reprodukcí problému ke stažení.
Friday, 24 February 2012 22:06:21 (Central Europe Standard Time, UTC+01:00)
.NET Framework | C# | Entity Framework | LINQ
Tuesday, 06 December 2011
Saturday, 26 November 2011
Prezentace z MS Festu 2011 a stručný komentář
Dnes jsem měl dvě přednášky na MS Festu.
Vývoj WP 7 aplikací pro pokročilé
Strasti a slasti vývoje wp7 aplikací. I Mango chutná hořko sladce.
Prezentace z přednášek naleznete níže.
Chci poděkovat lidem, kteří MS Fest připravují (klobouk dolů, v jakém počtu to zvládnou) a kteří mě oslovili a dovolili mi dnes přednášet. MS Fest se koná ještě zítra, pokud můžete, vyražte. Třeba přednášky o WCF RIA Services, PRISMu nebo přednáška o psaní testovatelného kódu budou určitě stát za výlet do Prahy.
Mně se jen potvrdilo, že na odbornou přednášku (ta první) je jedna hodina málo a na bulvární přednášku (ta druhá) až moc.
Jsem moc rád, že jsem viděl známé tváře a že jsem se seznámil s mnoha zajímavými lidmi. Omlouvám se těm, kteří se ke mně ani o poslední přestávce nedostali, nebylo v mých silách jít ke každému a zodpovědět všechny dotazy. A děkuji i za hromadné odpolední follow na twitteru, sice nevím, co to přesně znamená, ale to neznačí, že si toho nevážím.
Také chci poděkovat pánovi z Nokie (omlouvám se, neslyšel jsem v tomu hluku vaše jméno), díky kterému jsem si na přednášce procvičil asertivní chování.
Pár dalších postřehů, abych nemusel odpovídat jednotlivě na Twitteru a na G+:
-
Názory na druhou přednášku se liší, což se dalo čekat. Třeba
https://twitter.com/#!/WindMobiDown/status/140475583082663936 X
https://plus.google.com/104228058858941704434/posts/RxgW6HUtsibTakže malý komentář: Když jsem nabízel témata pro MS Fest, bylo zřejmé, že nebudu chválit. Na webu se dá dohledat dost mých vyjádření k WP7, ze kterých plyne, že ze současné podoby WP7 nejsem nadšený. Také jsem si uvědomoval, že na MS Fest se přednáška nemusí hodit – jedná se o akci zaštiťovanou Mícrosoftem a ani předpokládaní účastníci MS Festu nemusí být nadšeni. Ještě během podzimu jsem tyto své pochyby konzultoval s jedním z pořadatelů, s Tomášem Hercegem, ale shodli jsme se, že Microsoft určitě pár nekorektních slov na svou adresu přežije. Orel much nelapá a Microsoft podle mě nikdy moc přecitlivělý na kritiku nebyl.
-
-
Kdybych měl mít jen tu druhou přednášku na MS Festu a nebyla mi schválena první přednáška, ani ta druhá by nezazněla. Neměla by smysl. Počítal jsem s tím, že na obou přednáškách budou stejní lidé a že po přednášce, která o WP7 pojednává s plnou vážností, přijmou i tu, u které je i doprovodná prezentace zcela jiná. Na začátku jsem také zmínil, že kromě faktů k API WP7 pronesu subjektivní postřehy, nekorektní poznámky a domněnky, které mohou účastníci ignorovat. A uváděná fakta, jestliže mají lepší informace, opravit. Skoro na konci prezentace jsou v kontrapozici můj sarkastický pesimistický výrok a nadšený (nadsazený?) výrok Radka Hulána – i to jsem považoval za jasný signál, že prezentace je názorově jednostranná (kdo zná Radka, pochopil) a korektivem k ní byla první přednáška.
-
Pokud to zaniklo, tak leitomotivem druhé přednášky bylo:”Otravujme jako vývojáři Microsoft, ať nám ve WP7 nehází drobky ze stolu, ale poskytne nám servis, na který jsme zvyklí.”
A ještě. Záznam z mých přednášek nebude. Bohužel informace o natáčení byla rozesílána až tento týden, navíc jsem před přechodem na nový notebook a nechtěl jsem riskovat, že na můj již tak zkoušený stávající notebook nainstaluju nějaký SW, o kterém moc nevím. Mám špatnou zkušenost z instalace nějakého prezentačního SW, který mi na inhouse kurzu poté nedovolil přepnout výstup na projektor. Na veřejné a jen hodinové přednášce by to mohl být ještě větší průšvih. Ani mi to ale moc nevadilo, protože během tohoto podzimu se neustále potýkám se ztrátou hlasu a dalšími radostmi, takže jsem se bál, že můj výkon dotčený soustředěním na namáhané hlasivky bude navíc ještě zvěčněn na nějakém videu.
Díky, že jste přišli!:)
Saturday, 26 November 2011 20:15:12 (Central Europe Standard Time, UTC+01:00)
Silverlight | WP7
Tuesday, 30 August 2011
Sunday, 05 June 2011
O špatně chápaném principu jedné odpovědnosti třídy (SRP) a o zneužívání myšlenek Domain driven designu (DDD)
Dnes na twitteru David Grudl odkázal na debatu, která se týká vlastností v PHP. O vlastnostech v PHP mluvit nechci, ale v tomto příspěvku se chci dotknout některých “dogmat”, které se ozývají stále častěji a které byly použity jako univerzální kladivo na oponenty i v odkazované diskuzi.
Jedno zvláštní dogma se týká principu jedné odpovědnosti třídy (Single responsibility principle). Tento princip říká, že třída by měla mít jednu přesně vymezenou odpovědnost, která je v souladu s jejím názvem. I když na první přečtění se tento princip zdá neproblematický, dá se zneužít jako univerzální kladivo. Dogmatici mi říkali, že jedna odpovědnost si vynucuje, aby třída vždy měla právě jednu metodu, která tuto odpovědnost realizuje. Není nad přehledný svět objektových dogmatiků, kde objekt je jen stupidní kontajner na jednu (de facto globální?) funkci.
Dogmatiky tohoto zvláštního ražení zanedbejme jako ztracené případy a SRP obohaťme o další vysvětlení, které říká, že třída by měla mít jen jeden důvod ke změně. Tento princip je užitečný v tom, se snaží z aplikací odstranit všemocné božské (God) objekty, které mnohdy už svým názvem signalizují, že řeší spoustu věcí. UniversalOrderAndInvoiceProcessor oznamuje, že se bude měnit nejen, když se změní zpracování objednávek, ale také když se změní zpracování faktur. Jednoduché, což? Proč o tomhle jednoduchém principu vůbec dále mluvit?
V diskuzi se o SRP mluví (viz i příspěvky níže), ale diskutující tam ve své argumentaci používají něco, čemu na kurzech u SRP říkám falešné alternativy.
Mějme stejně jako v diskusi svou třídu Image, která nese informace o obrázku. Obrázek chceme uložit.
Varianta 1, kdy obrázek nese informace a současně nabízí metodu Save, ve které uloží data do souboru.
Co je v diskuzi vyčítáno této třídě? Porušuje princip jedné odpovědnosti, protože podle některých (Jiří Knesl, Ondřej Mirteš) řeší dvě věci najednou – nese data o obrázku a současně data ukládá. Souhlasím, že jde o porušení SRP, ale hlavním důvodem je to, že metoda Save je napsána tak, že třídu Image ukládáme vždy do souboru. Co když budeme chtít třídu Image uložit do nějakého “response” streamu na webovém serveru, nebo uložit přímo do databáze? Tuto třídu skutečně budeme měnit ze dvou důvodů – jednou, když přidáme nebo odebereme informace o obrázku a také, když budeme chtít ukládat obrázek do databáze, musíme rozšířit stávající metodu Save, což povede k tomu, že metoda bude mít v sobě nějaký podivný switch a bude trpět smíšenou odpovědností, protože bude dělat několik věcí najednou, nebo můžeme přidat novou samostatnou metodu SaveToDb.
Jedinou (!?) alternativou v diskuzi k tomuto postupu je vyvedení odpovědnosti za ukládání do různých úložišť do samostatných objektů, které mohou být skryty za jednotným rozhraním.
Toto řešení důsledně separuje odpovědnosti, navíc je velmi snadné přidat další implementaci rozhraní IImagePersistor, např. DbPersistor, který data uloží do databáze. Už v diskuzi Jakub Vrána ale upozorňuje na to, že se mu nelíbí, jak se řešení komplikuje pro uživatele-vývojáře, který s třídami bude pracovat, protože tento vývojář musí vědět, že existuje nějaký IImagePersistor/FilePersistor odpovědný za uložení dat. Třída Image nestačí k tomu, abyste dokázali vygenerovat data obrázku a uložit je, což může být ve vaší knihovně častý scénář. Také bych rád poprvé v tomto článku připomněl princip OOP, ke kterému se za chvíli vrátím, že objekt představuje jednotu svého stavu a chování, které je pro tento stav definováno.
Psal jsem o falešných alternativách, můžeme najít i jiná řešení. Co ponechat metodu Save ve třídě Image, ale z třídy Image udělat tzv kompozitor - objekt, který skládá své chování tak, že využívá další pomocné objekty, na kterých závisí, a nabízí intuitivní rozhraní pro klienty.
Odpovědnosti jsou stále separovány a dokonce třída Image, náš kompozitor, dodržuje pravidlo, které říká, že kompozitor by měl být jednodušší než suma funkcí jeho pomocných objektů. Klient třídy Image nemusí pracovat přímo s třídou FilePersistor, a přitom nemáme kód pro ukládání do souboru přímo ve třídě Image. Problém je, že metoda Save třídy Image vždy vytváří FilePersistor. Klient třídy Image si nemůže vyžádat to námi dříve zmiňované ukládání obrázku do databáze, a navíc třída Image závisí na jedné konkrétní třídě FilePersistor, u níž přímo volá konstruktor. V třídě Image mixujeme vytváření grafu spolupracujících objektů se samotným použitím pomocných objektů. Opět jde o dvě odpovědnosti, které bychom měli oddělit – SRP, nezapomeňme.
Nejprve ale zkusme vyřešit problém s tím, že klient nemůže ukládat data do databáze, protože třída Image ukládá data vždy do souboru.
Jednoduše přidáme další variantu metody, která přijímá odkaz na IImagePersistor, v našem případě třeba na DbPersistor. Původní metoda Save bez argumentů řeší ukládání do souboru. Ukládání do souboru je nejčastější scénář, který je zvolen jako výchozí. Stále ale tady máme problém s tím, že v metodě Save konstruujeme "natvrdo" FilePersistor. A navíc naše API klientům trochu lže. V podtextu klientovi sděluje, že výchozí metoda Save nemá žádné další závislosti, i když z implementace, !a jen z implementace!, je zřejmé, že jsme závislí na přítomnosti třídy FilePersistor. Poznámka: V C# 4 můžeme použít volitelné argumenty u jedné metody, ale na principu této varianty řešení se moc nemění.
Zkusme naše prozatím ulhané API vylepšit a dodržet SRP. Oddělme nyní konstrukci pomocných objektů, na kterých závisíme, od jejich použití v metodě Save.
Objekt Image si nyní v konstruktoru vynucuje předání IIMagePersitoru. Když klient IImagePersistor nepředá, objekt nezvznikne – sám konstruktor garantuje, že buď objekt Image má vyplněny všechny závislosti, nebo vůbec nevznikne. Vytvořili jsme konstruktor, který může použít a automaticky naplnit DI kontajner, nebo různé abstraktní továrny registrované v DI kontajneru apod. DI kontajner je přesně tím objektem, který by měl být v aplikaci odpovědný za konstrukci grafu objektů, v metodě Save objektu Image injektovaný IImagePersistor jen používáme. SRP v praxi.
Možný že ale v tomto případě je injektování závislostí přes konstruktor moc striktní. Co když nám skutečně vyhovuje, že můžeme bez DI kontajneru vytvořit objekt Image, který bude data ukládat do souboru. Pak můžeme využít injektování přes vlastnosti, kdy příslušnou vlastnost po vzniku objektu vyplníme rozumnou výchozí hodnotou – v našem případě instancí FilePersistoru. Poté ale platí, že třídu Image stále částečně zatěžujete konstrukcí objektů…
U většiny DI kontajnerů je preferováno injektování závislostí přes konstruktory, všechny, které znám, si ale poradí ale i s injektováním závislostí přes vlastností a u MEFu bych řekl, že injektování závislostí pomocí vlastností hrají prim.
Všechny tyto varianty mají své výhody a nevýhody a asi nemusím zdůrazňovat, že ani jedna není univerzálním kladivem. Varianty s injektováním závislostí (konstruktor, metoda, vlastnost) jsou samozřejmě mnohem lépe testovatelné.
Dokážu přidat i další příklady, ale chtěl jsem, abyste viděli, že SRP není ani nesmysl, ale ani princip, který by, podobně jako to zaznělo v diskuzi, sděloval – existují jen dvě alternativy, jak rozdělovat odpovědnosti, a ZROVNA TA TVOJE JE ŠPATNĚ.
A poslední poznámka:
Jiřé Knesl také v diskuzi uvedl: “ objekt buďto data reprezentuje (pak má settery/gettery), nebo vykonává činnost (pak dostane data parametry)”. Tohle je podle mě postoj blízký hlavně některým Javistům, o čemž svědčí i podle mého soudu schematický a nevěrohodný článek, který se zabývá vlastnostmi v Javě a na který se J. Knesl odkazuje. Znovu připomínám, že objekt představuje jednotu svého stavu a chování, které je pro tento stav definováno. Objekt, který má jen gettery a settery, je ”krabičkou na data”, pouhou strukturou známou i z neobjektových jazyků, a když má objekt jen metody, tak jde o (v mnoha případech skutečně globální) funkce/procedury, které prefixujeme názvem proměnné/třídy. V diskuzi to myslím nezaznělo, ale když někdo razí tuto drastickou separaci chování od samotných dat, často dodává, že takto je to přece definováno Evansem, tedy autoritou, v kanonické knize o Domain Driven Designu. Když se ptám, kde o tom Evans mluví, dozvím se, že Evans má objekty, které mají svůj stav (vlastnosti) a s objekty pracují speciální business-doménové služby (chování). I když mám vůči DDD spoustu výhrad, zde Evanse špatně interpretují – Evans by model, kde objekty mají jen stav a nemají žádné chování, nazval anemickým modelem – izolovaná data podepřená berličkami nesouvisejících globálních funkcí. Business služby jsou, zjednodušeně řečeno, určeny pro zapsání složitější business logiky, na které spolupracuje více objektů a žádný participující objekt není sám o sobě přirozeným kandidátem, do kterého by bylo vhodné logiku situovat.
Zde bych mohl pokračovat dále k rozdělení objektů v DDD, ke skutečnému významu vlastností u objektu, co říká princip “tell, don't ask”, ale už teď mi původně krátký komentář k SRP a DDD až moc nabobtnal. Když budete mít zájem o další naznačená témata, napište prosím komentář k článku.
Sunday, 05 June 2011 21:13:04 (Central Europe Standard Time, UTC+01:00)
Analytické drobky | C# | Návrhové vzory | UML
Monday, 30 May 2011
Pár triviálních poznámek k vývoji aplikací
Dostal jsme dotaz, jak si poradit s odstraňováním chyb u starší a rozsáhlé aplikace. Když jsem odepisoval, uvědomil jsem si, že sepisuju jakési “triviální desatero vývoje”", které se snažím už dlouhou dobu svému okolí vtloukat dohlavy. I když jde o triviální zásady, budu příště odkazovat raději na tento příspěvek, než abych vše opakoval pokaždé znovu. Jméno firmy je v textu nahrazenou souslovím “anonymní firma”.
“Zdravím,
to je na hodně dlouhý příspěvek.
Alespoň tedy:
1) Je nutné zrušit umělou hranici mezi vývojáři a testery. Žádná výměna informací přes šéfy oddělení nebo pověřené osoby.
2) Na každou objevenou chybu musí být napsán automatický test, který zajistí, že chyba neprobublá do dalších releasů. Bez toho žádné organizační opatření nefunguje. Bez napsaného testu se chyba nepovažuje za odstraněnou, ale jen za náhodou se nyní neprojevující.
3) Vytvořit malé sebeorganizující týmy odpovědné za určitou část projektu (nastálo, nebo do dalšího releasu). V čele team leader, který garantuje kvalitu. Team leader je k dispozici i testerům a řeší nesrovnalosti v analýze a systémovém designu. S dalšími team leadery řeší problémy integrace různých částí projektu. Team leader ale stále většinu času kóduje, není to embryo vychovávané pro střední management.
4) Je potřeba postupně napsat velké množství automatických testů (unit, integrační, akceptační) tak, aby se testeři věnovali hlavně novým záležitostem v releasu a aby vývojáři ani testeři nebyli obětí "ručně prováděných" regresních testů, které mají formu nikdy nekončícího debugování. Tím se i zkrátí doba, kterou "anonymní firma" nutně musí trávit opakovaným debugováním a "ručním" nalézáním příčin chyb. Automatizované testy představují práci, která se na projektech vyplatí, a navíc jde i o mnohem levnější řešení problému, než nabírání dalších a dalších testerů.
5) Nedávat žádné fixní odhady na odstranění chyb ani nikoho exkluzivně nealokovat jen na odstraňování chyb. Vývojář není a přes různé manažerské poučky ani nebude anonymní zdroj, který sebereme z jiného projektu, posadíme k aplikaci, kterou nezná, ale u které dostane befelem, že za jednu normohodinu musí odstranit 20 bugů. Tato kouzla fungují jenom v Excelu. V reálném světě vývojář těch 20 bugů neodstraní, ani když ho posadíte do open space, který je oblepen motivačním majstrštykem vytisknutým z PowerPointu nejlepším absolventem MBA.
Pokud se objeví chyba, chopí se jí člověk, který za danou oblast odpovídá (konflikty přinejhorším vyřeší team leadeři). Vývojář neustále čte a refaktorizuje kód, pokud možno ihned také odstraňuje chyby . A jsme opět u testů - dokud ty automatizované testy mít nebudete, vývojáři do kódu raději nezasahují, protože nevědí, kde všude se změna projeví a raději neriskují další možnou příčinu pádu aplikace po nasazení u zákazníka. A psát použitelné, ne jen formální-švejkovské testy, kdy se hodnotí "jen code coverage", se musí všichni vývojáři naučit a nějakou dobu to zabere.
Mám zkušenost s 12 let starou aplikací psanou původně pro VB, poté čátečně přepsanou na .Net Framework, která byla po 9 letech postupně obalena testy, a ani dnes sice nejde o žádnou vývojářskou lahůdku, ale pracuje se na ní beze strachu, co při každé změně v aplikaci zničíme. Nic jiného než to, co píšu výše, se mi neosvědčilo.
A pak další záležitosti jako (nutné) bonbonky:
- Neztrácet čas “mergováním” změn v něčem tak zastaralém a nepohodlném jako je Subversion. V Mercurialu (GITu) je propagace změn z vývojové větve do hlavní (a zpět) otázka chvíle. V Subversion ani TFS jsem po pár zkušenostech raději moc "branchů" nedělal.
- Automatické buildy spojené s již napsanými testy.
- Automatické nasazení nové verze aplikace, aby testeři nemuseli pátrat, kde seženou novou verzi a také, aby nasazení do produkčního prostředí neznamenalo 3denní práci party lidí, kteří se metodou pokus-omyl snaží dostat aplikaci do použitelného stavu u zákazníka.
- Alespoň u nováčků code review, abyste rychle odstranili jejich špatné návyky.
- Statická analýza kódu.
A dovolím si jednu soukromou poznámku k "anonymní firmě" - pokud možno zredukovat/vyhodit všechny těžkotonážní, neskutečně drahé a pro vývojáře zabijácké nápady se zavedením nejhorší možné formy vodopádu - analýza->samostatný systémový design->vývoj->testy, v jehož bludném pádu se bude produkovat množství dokumentů, navíc rychlokvašenými analytiky/designery, v mizerné kvalitě a bez vazby na skutečné potřeby projektu. Analýza a systémový design jsou fáze projektu, které pomáhají jen do doby, než se jich chytí nějaký exot, který nikdy žádnou aplikaci nevyvíjel a který si myslí, že analytická práce spočívá ve štosování nahodilých myšlenek zákazníka do příslušné šablony pro use case. Což je dle mých zkušeností většina “čistých”, míněno vývojem nedotčených, analytiků na pracovním trhu.
Monday, 30 May 2011 09:31:24 (Central Europe Standard Time, UTC+01:00)
Analytické drobky | Návrhové vzory | Ostatní | UML
Monday, 16 May 2011
Pozvánka na kurzy objektových principů a návrhových vzorů – podzim 2011
Opět vás všechny zvu na pravidelný podzimní běh kurzů Objektovými principy a návrhovými vzory řízený design a vývoj kvalitních aplikací 1 a Pokročilé návrhové vzory a objektové principy 2.
Novinkou v tomto roce je kurz pro začátečníky nazvaný Základy objektově orientovaného návrhu a vývoje. Někteří z vás ho mohli během posledních dvou let absolvovat ve formě inhouse kurzu pod interním názvem UML 0. Kurz UML 0 vznikl velmi spontánně jako reakce na požadavky některých firem, které nechtěly rovnou absolvovat stávající kurzy. Nyní je vycizelované UML 0 dostupné i jako veřejný kurz, protože v emailech se opakovaly žádosti o jeho veřejnou formu od lidí, kteří kurz absolvovali a nyní na něj chtěli poslat své kolegy.
O tomto novém kurzu naleznete podrobné informace níže v této pozvánce. Znovu opakuji, že se jedná o kurz pro začátečníky, který dříve v nabídce nebyl, protože kurzy Objektovými principy a návrhovými vzory řízený design a vývoj kvalitních aplikací 1 a Pokročilé návrhové vzory a objektové principy 2 byly a stále jsou určeny pro lidi, kteří základy znají. Nenechte se ale zmást “kódovým” názvem nového kurzu (UML 0), účastníci bývají překvapeni, že s UML na kurzu zacházíme zcela pragmaticky a bez posvátné úcty, ale i bez módních předsudků a rychlých odsudků. UML bereme jako nástroj, a ne jako samoúčelný cíl našeho snažení na kurzu.
Veřejný kurz Základy objektově orientovaného návrhu a vývoje (UML 0)
Datum konání kurzu: 19. 9. – 21. 9. 2011
Místo konání:
Školící středisko Tutor
U Půjčovny 2
110 00 Praha 1
Po celý den máme k dispozici wifi připojení a samozřejmě také teplé a studené nápoje. V ceně kurzu jsou obědy v hotelu.
Podrobné informace o kurzu a možnost přihlásit se na kurz
Program kurzu
FAQ - často kladené dotazy ke kurzům
Veřejný kurz Objektovými principy a návrhovými vzory řízený design a vývoj kvalitních aplikací 1
Datum konání kurzu: 10. 10. – 12. 10. 2011
Místo konání:
Školící středisko Tutor
U Půjčovny 2
110 00 Praha 1
Po celý den máme k dispozici wifi připojení a samozřejmě také teplé a studené nápoje. V ceně kurzu jsou obědy v hotelu.
Podrobné informace o kurzu a možnost přihlásit se na kurz
Program kurzu
Výběr z ohlasů na kurz
FAQ - často kladené dotazy ke kurzům
Veřejný kurz Objektovými principy a návrhovými vzory řízený design a vývoj kvalitních aplikací 2
Datum konání kurzu: 24. 10. – 26. 10. 2011
Místo konání:
Školící středisko Tutor
U Půjčovny 2
110 00 Praha 1
Po celý den máme k dispozici wifi připojení a samozřejmě také teplé a studené nápoje. V ceně kurzu jsou obědy v hotelu.
Podrobné informace o kurzu a možnost přihlásit se na kurz
Program kurzu
Výběr z ohlasů na kurzy
FAQ - často kladené dotazy ke kurzům
Program nového kurzu Základy objektově orientovaného návrhu a vývoje (UML 0)
Předpokládané znalosti účastníků
- Základní přehled o etapách vývoje projektu.
- Vhodné je mít nějaké vlastní zkušenosti z analýzy a vývoje projektů, abychom mohli porovnat různé postupy a doporučení a jejich použitelnost v praxi.
- Chuť se učit.;) Schopnost pohlédnout na některá domněle známá témata bez předsudků.
- Částečná znalost UML je vhodná, ale u tohoto kurzu není vyžadována. Vhodná je hlavně pro konfrontaci vlastních zkušeností s tím, co zazní o použitelnosti různých částí UML na kurzu.
- Nenávist ke kariéře zručného "bušiče", nikým nerespektovaného projektového vedoucího (tzv. sekretářky 2.0) a neschopného analytika / vývojáře.
Obecné informace ke kurzu
Pro vývojáře se u konstrukcí a prvků jazyka UML, které jsou považovány za analytické, dělají časté odbočky do kódu, aby vývojáři pochopili, že UML ani principy OOP nejsou nějaké nesmyslné abstrakce, ale užitečné konstrukce, které sami v programovacích jazycích používají denně. U role analytika stále zdůrazňuji, jaké znalosti z oblasti vývoje aplikací musí analytik mít, aby byl pro projekt užitečný a nevytvářel jen dokumentaci pro dokumentaci, kterou vývojáři nevyužijí a (až příliš často oprávněně) ji považují za nesmyslnou, drahou a hlavně vyvíjenému projektu nic nepřinášející. Kurz je určen pro vývojáře, systémové designery, analytiky a projektové manažery, kteří se chtějí seznámit se základními principy objektového programování a s modelováním v jazyce UML.
Program školení
- Proč má dnes UML v některých kruzích špatnou pověst? Musíte být těžkotonážní rytíři cválající na drahých CASE nástrojích a neživotných formálních projektových metodikách, nebo si vystačíte s nástroji a postupy, které zachytí vaše myšlenky, ale nenutí vás přizpůsobovat se "Jen Tomu Jedinému Pravému Stylu Návrhu A Vývoje"?
- Dostali jste poptávku, máte za pár dní dodat nabídku a v seznamu svých dovedností nenalézáte jasnovidectví ani nemáte po ruce čarovnou skleněnou kouli, abyste zákazníkovi do nabídky vyvěštili konečnou cenu i datum dokončení projektu? Co dělat v počáteční fázi projektu, kdy ještě ani nevíte, jestli budete projekt vyvíjet?
- Požadavky na systém. Jak je to s případy užití? Má vlastní zrychlená funkční specifikace bez zbytečných formalit.
- Rozsáhlé ukázky fukčních specifikací z projektů.
- Diagram tříd v UML.
- Rozdíl mezi analytickým diagramem tříd a diagramem tříd vytvářeným ve fázi systémového designu - existuje takový rozdíl, nebo jde jen o další hloupý mýtus?
- Třída - základní principy OOP, operace, atributy, viditelnost členů třídy.
- Vztahy mezi elementy diagramu (asociace, agregace, generalizace, závislost, realizace) – vše vykládáno na konkrétních příkladech z praxe + ukázky nejčastějších chyb, se kterými jsem se setkal.
- Dobře zapamatovatelné principy návrhu známé pod zkratkou SOLID v příkladech. Unit testy, integrační testy, akceptační testy - skutečně si stále myslíte, že se bez nich na projektech obejdete?
- Nejen SOLIDní principy stojí v pozadí návrhu aplikací...
- Nenásilný přechod k jednoduchým návrhovým vzorům.
- Příklady složitých diagramů tříd. Jak je udržovat v souladu s napsaným kódem? A musíme je udržovat? Co se na projektech vyplatí dělat a co projekt spolehlivě zabije?
- Objektový diagram + příklady.
- Diagramy a diagramy interakce. Příklady. Typy projektů, pro které se tyto diagramy hodí.
- Vysvětlení stavových diagramů + výhody aplikací řízených přesně definovanými stavovými automaty.
- Diagram aktivit - modelování složitých business procesů v organizaci. Hrajete si s diagramem aktivit rádi, ale ocení to Váš zákazník?
- Vrstvy a moduly v aplikaci – architektura aplikace.
- Relační a objektový svět? Stále jediné možné partnerství z rozumu?
- Nasazení aplikace – výklad Component & Deployment diagramů.
- OOP a jeho vztah UML. Vztah UML a OOP k projektové praxi a realitě.
- Výhody a nevýhody UML – vyzdvižení nejvíce používaných postupů, odhození nepotřebné veteše z jazyka UML.
- Odpovědi na dotazy frekventantů kurzu.
Těším se s Vámi na setkání na kurzu!
René Stein
Monday, 16 May 2011 11:15:38 (Central Europe Standard Time, UTC+01:00)
Analytické drobky | Kurzy UML a OOP | Návrhové vzory | UML
Monday, 21 March 2011
Prezentace Moderní trendy ve vývoji aplikací
Přibližně před rokem jsem u dvou firem začínal sérii technologických kurzů subjektivním shrnutím změn (nejen) v aplikacích psaných v .Net Frameworku. Nedávno jsme ji s kolegou náhodou otevřeli a pobavili jsme se nad tím, jak je rok v IT stále dlouhá doba a že zde dvojnásobně platí “tempus fugit”. Napadlo mě, že se nad prezentací možná se pobaví i někdo další, hlavně v pasážích, kde jemně naznačuju zálibu Microsoftu v zařezávání technologií.
U prezentace je třeba mít na paměti:
- Jedná se jen o osnovu “přehledové“ a cca dvouhodinové přednášky.
- Témata, typy projektů a technologie jsou v přednášce voleny podle zájmu zákazníka.
- Snažil jsem se nebýt hned v této úvodní přednášce příliš ostrý a konfliktní.
- Zvolená témata se týkala oblastí, které jsme v dalších dnech probíraly detailněji na konkrétních projektech vytvořených na návazných kurzech. Po pár zkušenostech si myslím, že jediný smyslupný kurz zabývající se technologií či programovacím jazykem je ten, na kterém píšete před účastníky kód. Tato přednáška byla koncipována jako motivační úvod k dalším tématům.
Monday, 21 March 2011 13:11:41 (Central Europe Standard Time, UTC+01:00)
.NET Framework | ASP.NET | C# | Compact .Net Framework | LINQ | RX Extensions | Silverlight | Web Services | Windows Forms | WP7
Tuesday, 15 February 2011
FAQ k WP7 a pár specialitek navíc – aneb co jste vždy chtěli vědět o WP7, ale v Microsoftu se vám báli odpovědět
Na twitteru i v emailu se opakuje stále několik dotazů od lidí, kteří koketují s myšlenkou pořídit si WP7 telefon nebo chtějí pro WP7 vyvíjet. Účelem tohoto článku není suplovat uživatelský informační servis o WP7, který u nás skvěle dělá Smartmania a kterému se i v zahraničí věnuje mnoho webů, ale nabídnout snad trochu jiné informace než ty, které různí nájemní pisálci stále kopírují z tiskových zpráv a oficiálních prezentací a které další sociálně-altruističtí trotlové bezmyšlenkovitě linkují, sdílejí a lajkují.
Je Windows Phone 7 zcela nový operační systém, který Microsoft skutečně vyvinul “na zelené louce” a který nemá NIC společného s Windows Mobile?
Je i není. Neuspokojivá odpověď ve stylu hárající chytré horákyně, já vím. Windows Phone 7 jsou postaveny na operačním systému Windows CE, na kterém běžely i Windows Mobile. Dnešní oficiální název zní Windows Embedded CE a aktuální verze má pořadové číslo 6. I Windows Mobile v poslední verzi 6.5 byly stále založeny na Windows CE 5. Jak vidíte, Microsoft nám to moc neulehčuje a číslo verze Windows Mobile není nijak závislé na verzi použitých Windows CE. WP7 dle všech indicií běží na buildu Windows CE 6.
Windows CE 6 mají oprotí Windows CE 5 několik výhod. A z toho plyne, že i WP7 využívají nových rysů v systému a netrpí hlavními problémy Windows Mobile aplikací.
- Ve Windows CE 5 mohlo běžet najednou maximálně 32 procesů. Ano, celých 32 procesů musí stačit každému, ale s tím, jak se smartphony/PDA/MDA vyvíjely a výrobci ihned po startu zařízení obsazovali stále více procesních slotů pro své a se zařízením dodávané aplikace, se tento limit kupodivu ukázal nedostačující. Podpora cca 32 000 procesů ve Windows CE 6 je pořádné zlepšení.
- Ve Windows CE 5 měl každý proces 32 MB virtuální paměti a všechny procesy sdílely 4 GB adresní prostor. Nechci zabíhat do detailů, ale způsob nahrávání dynamických knihoven a jejich sdílení mezi procesy mohl vést velmi rychle k fragmentaci paměti a k chybě “nedostatek paměti”, i když fyzické paměti bylo dostatek. V CE 6.0 jsou 2 GB virtuálního paměťového prostoru vyhrazeny pro kernel a 2 GB pro každý proces. Správa paměti v CE 6 má některé nepříjemné dopady na vývojáře kernel/user driverů, ale pro uživatele i vývojáře aplikací je tento paměťový model mnohem lepší.
Mobilní Silverlight ve WP7 interně využívá Compact .Net Framework, který můžete znát z Windows Mobile. Jestliže pro WP7 vyvíjíte, poznáte to i podle toho, že při ladění aplikace a pokusu editovat zdrový kód vám Visual Studio hlásí, že Compact .Net Framework aplikace nemohou být při ladění upravovány.
Shrnuto – Windows Phone 7 mají nový kabátek (UI Metro), nová běhová prostředí pro uživatelské aplikace (Silverlight, XNA Framework) a vylepšený podvozek (CE 6). Psát a hlavně v článcích zdůrazňovat, že WP7 nemají se staršími Windows Mobile NIC společného, mi přijde v lepším případě naivní, v tom horším okázale neprofesionální. Už minulý rok jsem psal několikrát , že Microsoft by těžko za pár měsíců vyvinul zcela nový mobilní operační systém.
Mohu v ČR nakupovat aplikace v marketplace?
Stručná odpověď: Ne.
Složitější odpověď. Microsoft podle neoficiálních informací uvažuje, že marketplace v ČR bude dostupný “v létě 2010 2011”.
Neoficiálně to jde – stačí mít zahraniční platební kartu, založit nějaký Live účet, kde nastavíte, že pocházíte třeba z USA/UK. Ale musíte být asi velcí blázni nebo nadšenci, abyste si přes tyto obstrukce kupovali dnes (psáno 15.2. 2011) v ČR WP7 telefon. Počítejte také s tím, že když Microsoft zpřístupní marketplace pro ČR, a vy budete chtít používat svůj běžný Live účet, zakoupené aplikace mezi různými Live účty nepřevedete.
Mohu dnes dát svou WP7 aplikaci do marketplace?
Stručná odpověď: Ne.
Složitější odpověď. Pokud chcete dát aplikaci do marketplace, vyvíjejte se zahraničním partnerem. Je možné také se s trochou kreativity při tvorbě vývojářského účtu (v komentářích tohle sousloví vysvětlovat raději nebudu) registrovat jako vývojář z jiné země, pokud máte zahraniční platební kartu, ale riskujete, že Microsoft na tento “trik” přijde a zablokuje vám prodej aplikací, protože porušujete jeho podmínky.
Dobře, jsem smířen s tím, že se do market place prozatím nedostanu, ale přesto chci svou aplikaci otestovat na reálném zařízení, a ne jen v emulátoru. Jak to udělám, když mi WP7 telefon nedovolí nahrát vlastní aplikace?
Nejjednodušší je dnes použít Chevron WP7 k neoficiálnímu odemknutí telefonu. Poté co telefon odemknete, můžete nahrávat své vlastní aplikace. Podrobný návod, jak odemknout telefon, je na XDA-Developers, takže jej zde nebudu opakovat. Autoři Chevronu používají na odemčení WP7 telefonu jednoduchý trik. Požadavek na odemčení telefonu cílený na servery Microsoftu přesměrují na lokální počítač a odemčení telefonu povolí. Výhodou je, že nejde o vážný zásah do zařízení, nevýhodou, že pro Microsoft bude snadné tuhle “díru” (dá se pro tohle amatérské zabezpečení najít nějaký jiný eufemismus?) uzavřít další plánovanou aktualizací. Microsoft ale po setkání s autory Chevronu přislíbil, že sám umožní “co nejdříve” vývojářům nahrání a ladění vlastních aplikací v telefonu, aniž by bylo nutné otevírat a platit za vývojářský účet. Neznámou proměnnou je, které jednotky pro měření času používá Microsoft, kde, soudě podle stále odkládaného data první aktualizace pro WP7, korporátní čas evidentně plyne pomaleji, a jestli optimistická fráze “co nejdříve” zahrnuje alespoň předpokládanou dobu životnosti WP7 na trhu smartphonů.
Mohu pro WP7 vyvíjet v Compact .Net Frameworku?
Ne. I když Microsoft Compact .Net Framework používá a při nahrání Silverlight aplikace je patrné z debug logů, že se nahrávají CNF assembly, vlastní CNF aplikaci nenapíšete. Ale – v poslední části tohoto FAQ o prozatím hypotetické možnosti spouštět CNF aplikace na WP7 zařízení znovu píšu.
Mohu pro WP7 vyvíjet nativni (C/C++) aplikace? Slyšel jsem o nějakém Microsoftem vyvinutém nativním frameworku Iris.
Stručná odpověď: Nativní aplikace nejsou podporovány, můžete vyvíjet jen Silverlight aplikace nebo aplikace využívající XNA Framework. Na Iris v klidu zapomeňte.
Složitější odpověď: Nejprve vyřídíme IRIS, což je framework, ke kterém jsem se také vyjadřoval na twitteru.
Twitter: Objevily se bajky o tajném nativním frameworku IRIS pro #WP7. IRIS ale připomíná spíš mix CNF/WPF + P/Invoke. Nativní kód =stále WIN CE API |
Zde jen dodám, že IRIS si můžete představit jako framework, který v MS vyvinuli vývojáři, kteří asi nesnášejí technologie WPF/XAML/Silverlight vytvořené jejich kolegy, a proto si navrhli vlastní značkovací jazyk mixovaný s řízeným kódem a množstvím API (P/Invoke) volání. IRIS Microsoft pravděpodobně používá v shellu a ve svých WP7 aplikacích. Jestliže jste se ptali, jak to, že u MS aplikací třeba prvky Pivot a Panorama podporují plynulejší skrolování než stejné prvky v Silverlightu, v použití IRIS frameworku leží pravděpodobně odpověď. V Microsoftu buď občas neví levice, ce dělá pravíce, anebo mobilní Silverlight není zas tak odladěn a vyšperkován, jak by se mohlo z prezentací demo aplikací u MS evangelistů zdát, a teprve IRIS je tím zázrakem, který rozanimuje výchozí uživatelské rozhraní a dovede recenzenty WP7 systému k stále opakované větě o “ďábelské rychlosti WP7 aplikací”. Věta “o ďábelské rychlosti WP7 aplikací” platí paradoxně do té doby, než začnete další aplikace skutečně instalovat, k čemuž se nadšení recenzenti už asi nedokopou. Trochu sarkasticky dodejme, že v bajtech zhmotněná IRIS snad není ve WP7 proto, aby poslala brzy bezduché WP7 do křemíkového pekla, stejně jako kdysi mytologická Iris pomohla Dídó uvolnit duši z těla.
Nativní kód ale některé aplikace napsané v Silverlightu používají. Jestliže si prohlédnete XAP soubory (XAP soubor – distribuční soubor Silverlight aplikace) aplikací od výrobcu zařízení, zjistíte, že používají knihovnu GAC_Microsoft.Phone.InteropServices_v7_0_0_0_cneutral_1.dll. Jak název assembly napovídá, měla by sloužit jako most k nativnímu kódu.
Aplikace od výrobce tuto assembly používají pro dynamické volání metod COM objektů. Zkusil jsem si napsat jednoduchou COM assembly ve VS 2008 pro Windows CE, protože VS 2010 z rozhodnutí nějaké moudré hlavy ve vývojářské centrále Microsoftu podporu pro vývoj CE aplikací již neobsahuje. COM knihovnu jsem nazval SL_COM2.dll.
Poté jsem založil projekt v Silverlightu, přidal do něj soubor nazvaný WPInteropManifest.xml, který obsahují i aplikace od výrobců. Soubor WPInteropManifest.xml má jednoduchý obsah.
Do projektu jsem přidal knihovnu GAC_Microsoft.Phone.InteropServices_v7_0_0_0_cneutral_1.dll. i svou COM knihovnu SL_COM2.dll a u obou knihoven jsem nastavil build akci na content, aby obě knihovny byly přidány do výsledného XAP souboru. Žádné COM Interop knihovny nevygenerujete, COM typy musíte naimportovat z COM knihovny “ručně”.
Pomocí reflexe zaregistrujeme COM knihovnu na cílovém zařízení s využitím metod v assembly GAC_Microsoft.Phone.InteropServices_v7_0_0_0_cneutral_1.dll.
Tento kód projde, ale na posledním řádku dostanete výjimku, která znamená, že vaše COM knihovna je nekompatibilní s aktuální verzí operačního systému. Musíte si na internetu “najít” a nainstalovat správný Platform builder, což udělal asi Ch. Welsh, nebo si pohrát s PE hlavičkami knihoven, což jsem udělal já. COM knihovna by mohla být po odstranění všech překážek i branou ke všem API funkcím, které si vystavíte v COM objektech. Nepředpokládejte ale, že vaše aplikace bude při využívání COM knihovny přijata do marketplace.
Když jsou problémy s umístěním aplikací do marketplace, je naděje, že brzy bude pro WP7 existovat “jailbreak” a my budeme mít obchod s neoficiálními aplikacemi? Dají se WP7 “hacknout”?
Všechno jde, jen to stojí čas a peníze. A musíte mít přitom víru, že WP7 z trhu rychle nezmizí.
Hlavní problém je podle mě v tom, že Silverlight aplikace běží ve striktním bezpečnostním modelu, který není snadné překonat. Takže možnosti, které mě napadají:
Metoda pokus-omyl – začneme hledat slabiny v zabezpečení celé CE platformy a doufat, že Microsoft někde udělal další školáckou chybu podobnou té, kterou využil Chevron, jen teď někde v mnohem nižších vrstvách systému. Třeba kdyby Microsoft ponechal i byť jen trochu pootevřen přístup k RAPI, to by byla paráda. Sice se dá “získat” a použít knihovna SmartDevice.Connectivity, která RAPI interně využívá, ale neodemčený telefon nespolupracuje.
Ukázka volání:
Daleko nadějnější mi přijde pokračovat tak, že se do CE registrů přidá vlastní driver, který po startu zařízení shodí výchozí WP7 shell a dovolí vám nainstalovat a spouštět nativní aplikace i aplikace psané v Compact .Net Frameworku. V dalším kroku by to chtělo nového hostitele Silverlight aplikací, který bude vstřícnější k neoficiálním aplikacím. Na některých HTC zařízeních se průnik do registru již podařil… Takže nezbývá než zopakovat: Všechno jde, jen to stojí čas a peníze.
Snad jste v tomhle článků nalezli alespoň něco zajímavějšího než v konfekčních článcích o WP7.
Tuesday, 15 February 2011 15:23:05 (Central Europe Standard Time, UTC+01:00)
Compact .Net Framework | Mobilitky | Silverlight | WP7
Monday, 24 January 2011
Tipy pro Windows Phone 7 aplikace V – vytváříme prvni aplikaci (a stavíme ji na vytvořených základech)
V předchozích článcích jsme si vytvořili miniframework pro view modely a ukázali si hostitele našich view modelů. Přišel čas naše znalosti, idiomy a návrhové vzory zakódované ve formě aplikační infrastruktury v našem miniframeworku využít při tvorbě konkrétní aplikace. Pro účely tohoto článku i následujících článků jsem se rozhodl, že vytvoříme aplikaci, která nám dovolí spravovat vlastní blog na doméně Posterous s využitím Posterous API.
Hlavní případy užití, které budeme v aplikaci podporovat.
- Přihlášení uživatele ke svému účtu - téma dnešního článku.
- Zobrazení seznamu blogů, které patří přihlášenému uživateli.
- Zobrazení seznamu příspěvků na vybraném blogu.
- Zobrazení detailu příspěvku na blogu.
- Úprava stávajícího příspěvku na blogu.
- Zadání nového příspěvku na blog.
Jako vždy nás tato prozatím letmo načrtnutá témata dovedou k dalším podivným zákoutím vývoje WP7 aplikací a my se z nich s úsměvem záludnostmi WP7 poučeného idiota zoceleného harcovníka pokusíme dostat..
Skutečně vás nechci urážet popisem klikání ve Visual Studiu ani popisem základů XAMLu, “data bindingu”, “behaviors”, takže jen napíšu, že byste před vytvářením aplikace měli:
- Stáhnout si a nainstalovat Windows Phone Developer Tools.
- Ve Visual Studiu založte nový projekt Windows Phone Application – nejlepší bude, když ho pojmenujete jako já RStein.PosterousReader.WP, abyste nebyli zmateni názvy jmenných prostorů dále v článku.
- Vývojářský život je na tomto projektu jednodušší o to, že k práci s Posterous API použijeme můj C# Posterous API Wrapper pro WP7. Po stažení přidejte referenci na knihovnu RStein.Posterous.API.SLM do svého projektu.
A i když to dnes ještě není tak nutné, zřiďte si na Posterous vlastní účet, abyste mohli aplikaci později testovat na reálných datech. C# Posterous API pro nás v triádě Model-View-ViewModel bude představovat model, což má pro nás výhodu, že se můžeme stále soustředit na view a view modely, o které šlo i v předchozích článcích, a model můžeme považovat za černou skříňku.
- V novém projektu vytvořte hlavně dvě nové složky View a ViewModels. Na obrázku jsou červeně podtrženy další složky, které si již dnes doporučuju přidat do projektu - Behaviors, Extensions, HostServices, Icons, SpecialTypes, UI, UIServices a hlavně ViewModels a Views.
- Předpokládám, že jste schopni do svého projektu vložit kód tříd, které jsem popisoval v předchozích článcích. Seznam předchozích článků s výpisy kódu naleznete na konci tohoto článku.
Dnes vytvoříme přihlašovací obrazovku do naší aplikace. Pro lepší představu je zde obrazovka, kterou byste měli mít hotovou na konci dnešního článku.
Nejprve určíme, které funkce musí obrazovka a její podkladový kód zvládat:
- Přihlašovací obrazovka se zobrazí ihned po startu aplikace.
- Uživateli dovolíme zadat přihlašovací jméno a heslo.
- Jestliže není uživatelské jméno vyplněno (prázdný řetězec) a/nebo není vyplněno heslo, tlačítko Přihlásit se je neaktivní.
- Jestliže dojde k “tombstoningu” stránky, bude jméno i heslo po návratu z “tombstonovaného” stavu zachováno – ověříme si tak poprvé in vivo, že naše třída ViewModelBase podporuje “tombstoning” přesně dle našich požadavků.
- Po návratu na přihlašovací obrazovku z jiné části aplikace bude zachován jen obsah textového pole “uživatelské jméno”, textové pole “heslo” bude prázdné.
- Po kliknutí na tlačítko Přihlásit nebudeme zadané jméno a heslo ihned ověřovat, ale uložíme oba údaje pro použití na dalších stránkách aplikace. Poté přesměrujeme uživatele na další stránku se seznamem blogů, kde bude jméno a heslo využito k získání seznamu blogů uživatele. Důvodem je to, že v Posterous API se jméno a heslo zasílá při každém webovém požadavku, žádné jednorázové přihlášení neexistuje a nemá smysl generovat nějaký “dummy” požadavek jen pro ověření hesla. Jestliže se na stránce se seznamem blogů, na kterou z přihlašovací obrazovky přesměrováváme, data kvůli neplatným přihlašovacím údajům získat nepodaří, aplikace nás vrátí na přihlašovací obrazovku. Vytvoření stránky se seznamem blogů bude téma dalšího článku.
A začínáme:
Do složky Views vložte nové View pro přihlášení uživatele. V dialogu Add new item vyberte Windows Phone Portrait Page a pojmenujte ji MainLoginView.xaml.
Jedná se o úvodní stránku aplikace, a proto v deskriptoru WP7 aplikace nazvaném WAMppManifest.xml, který naleznete v projektové složce Properties, změníme startovací stránku na View/mainLoginView.xaml.
I když to není u přihlašovacího dialogu nutné, v dnešním článku si ukážeme, jak můžeme jednoduše složit jedno view z dalších nezávislých view a také to, že view nemusí být jen stránka (Page), ale i “User Control”. Naše MainLoginView bude zobrazovat titulek aplikace (Posterous klient), titulek stránky (Přihlášení) , ale textová pole “heslo”, “uživatelské jméno” a tlačítko “Přihlásit" se” bude obsahovat “user control” LoginView, který můžeme použít jako součást i zcela jiného view (stránky) v aplikaci. Do složky Views přidejte Windows Phone User Control a nazvěte jej LoginView.xaml.
Zkompilujte (Build) “solution”.
Do view MainLoginView vložte tento kód:
Jak jsem zmiňoval v jednom z předešlých článků, všechny stránky v aplikaci by měly být potomkem naší třídy PageBase, která je hostitelem pro view modely. Proto je naše stránka uzavřena v elementu <controlex:PageBase>. XML jmenný prostor controleex je u mě namapován na xmlns:controlex="clr-namespace:RStein.PosterousReader.WP.UI". V projektovém adresáři UI musíte mít tedy třídu PageBase.
Také v “code behind souboru” musíte třídu MainLoginView učinit potomkem PageBase.
Hlavní xaml pro přihlášení uživatele obsahuje “User Control” LoginView, na který se v MainLoginView odkazujeme. (<views:LoginView Grid.Row="1"></views:LoginView>). XML jmenný prostor views je mapován na jmenný prostor RStein.PosterousReader.WP.Views v C# (xmlns:views="clr-namespace:RStein.PosterousReader.WP.Views) – v našem případě tedy na projektový adresář Views.
Do view s názvem LoginView vložte následující XAML:
Jak bylo vidět na snímku obrazovky na počátku článku, LoginView obsahuje hlavně textové pole pro zadání jména uživatele (txtName) a prvek pro zadání hesla uživatele (txtPassword) a tlačítko “Přihlásit se”. U prvků pro zadání uživatelského jména a hesla vás mohou zarazit jen tagy <i:Interaction.Behaviors> a atributy jako behaviors:TextboxPasswordAttachedProperties.TextBoxChangedAction, jejichž význam vysvětlím za chvíli. Také vás hned upozorním, že tlačítko “Přihlásit se” je speciální tlačítko ((<controlex:ButtonEx) dovolující reagovat na stisknutí tlačítka uživatelem tak, že spustí metodu Execute objektu ICommand, což standardní WP7 tlačítko nezvládá. Znovu připomínám, že Silverlight ve WP7 je bohužel založen na poměrně staré verzi 3 desktopového Silverlightu. Kód třídy ButtonEx uvidíte také za chvíli.
Máme view, ve view používáme “data binding”, ale nemáme ještě view modely, které fungují pro view jako zdroj dat.
V každém view modelu naší aplikace budeme používat objekt IPosterousApplication ke komunikaci s Posterous, budeme chtít z view modelů navigovat na další view v aplikaci a také bychom měli být schopni v každém view modelu získat uživatelské jméno a heslo zadané na na námi vytvářené přihlašovací obrazovce. To znamená, že společné vlastnosti a služby view modelů pro naši aplikaci můžeme soustředit v bázové třídě PosterousViewModelBase. Do projektové složky Views vložte novou třídu PosterousViewModelBase a do ní zkopírujte následující kód.
PosterousViewModelBase je potomkem naší staré známé třídy ViewModelBase
V konstruktoru třída PosterousViewModelBase přijímá odkaz na objekt IPosterousApplication, který je základním rozcestnikem pro přístup k Posterous API, navigační službu INavigationService, která view modelu dovolí navigovat na další view v aplikaci (např. ze seznamu článků na detail vybraného článku), a titulek zobrazené stránky. Titulek převezme a nabízí poté ve vlastnosti PageTitle bázová třída ViewModelBase.
Navigační služba je představována rozhraním INavigationService.
Základní implementace rozhraní INavigationService pro WP7 je dostupná ve třídě DefaultWPNavigationService a pouze obaluje navigační služby dostupné v samotných WP7 aplikacích.
Rozhraní INavigationService i třídu DefaultWPNavigationService vložte do projektové složky HostServices.Více si o navigaci mezi různými view povíme v dalších článcích.
Vraťmě se k PosterousViewModelBase. V PosterousViewModelBase máme vždy uloženo ve statických vlastnostech LastUsedUserName a LastUsedPassword poslední zadané přihlašovací údaje, které může každý view model využít při získání nebo úpravě dat z Posterous.
Poznámka: Kdyby někoho z vás pohoršovali statické vlastnosti, klidně si jako domácí úkol napište “CredentialsManager”, který bude injektován stejně jako již zmíněné dvě další služby do view modelů. Prozatím nechci komplikovat kód víc, než je nutné.
My máme dvě view, MainLoginView a LoginView, a proto vytvoříme i dva view modely. Opět zdůrazňuju, že tato volba je na vás a díky “dědění view modelů” bychom mohli ponechat třeba dvě view a vytvořit pro ně jen jeden společný view model.
Do projektové složky ViewModels přidejte třídu nazvanou MainLoginViewModel – view model pro MainLoginView. Název view modelu nyní musí odpovídat konvenci “NázevView +ViewModel”. Zdůrazňuju i zde, že jde jen o konvenci a že si můžete jednoduše napsat jiný IViewModelResolver, který dohledá dle vašich zcela jiných projektových konvencí k view vhodný view model.
MainLoginViewModel je sympaticky jednoduchý objekt. Má jen delegující konstruktor, ve kterém předá své bázové třídě PosterousViewModelBase vyžadované povinné argumenty posterousApplication, navigationService a titulek stránky.
Texty v aplikaci nejsou prozatím lokalizovány a titulky stránek jsou uloženy ve třídě GlobalConstants. Do “rootu” projektu přidejte třídu GlobalConstants.cs
Název aplikace (APP_MAIN_TITLE) , podobně jako titulek stránky, vydává ve vlastnosti AppTitle opět ViewModelBase
Jen o trochu složitější bude view model pro LoginView. Do projektové složky ViewModels přidejte třídu LoginViewModel.
V LoginViewModelu máme opět delegující konstruktor, jedinou změnou oproti MainLoginViewModelu je to, že jako titulek stránky předáváme prázdný řetězec, protože předpokládáme, že titulek na stránku dodá “nadřízené” view.
Vlastnosti a jejich význam
Název vlastnosti | Popis |
TextChangedAction | Akce, která má být vyvolána, když se ve view změní text přihlašovacího jména nebo hesla. Tato akce je v konstruktoru inicializována tak, že si vynutíme opětovné vyhodnocení toho, zda může být proveden LoginCommand. V článku popíšu, proč je to (prozatím?) řešeno takto. |
LoginCommand | Objekt podporující rozhraní ICommand, který v metodě Execute zavolá metodu handleLogin. Metoda handleLogin uloží zadané jméno a heslo do statických vlastností LastUsedPassword a a LastUsedUserName a přesměruje uživatele na stránku se seznamem blogů. Instanční vlastnost UserPassword je při každém pokusu o navigaci na stránku se seznamem blogů “vyčištěna” tím, že je do ní uložen prázdný řetězec, a při návratu na přihlašovací obrazovku tedy není automaticky předvyplněno heslo, což byl jeden z našich požadavků na přihlašovací dialog. LoginCommand může být proveden jen tehdy, když bylo ve view zadáno a do view modelu přes oboustranný (two way) binding zpropagováno uživatelské jméno a heslo. Pokud vlastnosti UserName a UserPassword mají hodnotu null nebo obsahují prázdný řetězec, LoginCommand nemůže být proveden. |
UserName | Zadané přihlašovací jméno na službu Posterous. V metodě DoInternalInit, která je volána na začátku životního cyklu view modelu předvyplníme uživatelské jméno posledním zadaným uživatelským jménem, které jsme dříve uložili do statické vlastnosti LastUsedUserName. |
UserPassword | Heslo na službu Posterous. |
Co potřebujeme, abychom mohli náš LoginViewModel zkompilovat?
Do třídy GlobalConstants přidejte URL dalšího view se seznamem blogů a článků.
Stránku PostsListView.xaml vytvoříme v dalším článku, nyní ji celou vytvářet nemusíte. Postačí do složky Views dát novou stránku (Page) s názvem PostsListView.xaml.
Jak jsem již psal, ve WP7 nemáme bohužel podporu pro objekty ICommand. Třída DelegateCommand, jejíž instancí je LoginCommand, je minimalistickou implementací rozhraní ICommand.
Pokud podobnou třídu nemáte, vložte si do projektu třídu DelegateCommand. U mě je v jmenném prostoru RStein.PosterousReader.Common.
Třídě DelegateCommand můžete předat dva delegáty. Delegát executeAction (“co má být vykonáno”) je spuštěn v metodě Execute z rozhraní ICommand. Delegát canExecuteAction představuje implementaci metody CanExecute (“může být nyní command vykonán?”). Minimalistická implementace je to proto, že nijak nepoužívám událost CanExecuteChanged, a namísto toho jsem si pro “binding” vystavil speciální vlastnost CanExecuteCommand, která v get akcesoru deleguje na metodu CanExecute.
Vlastnost TextChangedAction v LoginViewModelu je delegát typu Action, který nám pomáhá vyřešit jeden z požadavků na přihlášení.
“Jestliže není uživatelské jméno vyplněno (prázdný řetězec) a/nebo není vyplněno heslo, tlačítko Přihlásit se je neaktivní.”
To znamená, že potřebujeme po každém přidání nebo smazání znaku v textboxu pro zadání jména uživatele i v textboxu pro zadání hesla zjišťovat, jestli můžeme ve view zpřístupnit tlačítko pro přihlášení. Když je alespoň jeden textbox prázdný, tlačítko pro přihlášení není dostupné, když je vyplněno alespoň jedním znakem jméno i heslo, tlačítko pro přihlášení je dostupné.
Jak je tento požadavek ve view a view modelu splněn?
Ve view LoginView je vlastnost IsEnabled tlačítka “Přihlásit se“ z třídy ButtonEx “nabindována” na vlastnost CanExecuteCommand objektu LoginCommand ve view modelu.
Do projektové složky UI si přidejte třídu ButtonEx, která doplňuje standardní WP7 tlačítko o jednoduchou podporu objektů ICommand.
Vlastnost CanExecuteCommand musí vracet true, pokud je v každém textboxu alespoň jeden znak, jinak false. Jak ale ve view modelu zjistíme, že uživatel zadal nebo smazal v některém textovém poli další znak? Do view modelu se při oboustranném bindingu zpropaguje hodnota z textového pole až po opuštění textového pole uživatelem, ale my přitom musíme reagovat ve view modelu na zadání každého znaku.
Takové chování textových polí ve WP7 skutečně nemáme a musíme si ho dopsat, a to nejlépe za pomoci ”attached” vlastností a objektů Behavior<T>. Nejprve se podívejme, jak vypadají objekty TextBox a PasswordBox v XAMLu, když jsou rozšířeny o “attached” vlastnost TextboxPasswordAttachedProperties.TextBoxChangedAction, která je “nabindována” na nám již známou vlastnost TextChangedAction typu Action ve view modelu. “Attached” vlastnost TextBoxChangedAction tedy říká: “Hej, kdykoli se změní u prvku zadaný text, musí NĚKDO ochotný zavolat delegáta TextChangedAction, abychom ve view modelu nebyli odříznuti od novinek ve světě view.”
A ten někdo bude náš objekt Behavior , který zase světu sděluje: “Mám dobré vychování, a když mi dovolíte vstoupit do bran tagu PasswordBox nebo TextBox, delegáta TextChangedAction zavolám, i když to sám WP7 TextBox a PasswordBox nezvládne.”
Další nepříjemností ve WP7 je, že i když objekt Behavior má mít pro TextBox a PasswordBox stejné chování, musíme napsat dva objekty Behavior pro každý prvek, protože TextBox ani PasswordBox spolu kupodivu nemají moc společného. My se alespoň pokusíme kód v obou objektech Behavior neduplikovat.
Do projektové složky Behaviors přidejte třídu TextboxPasswordAttachedProperties a v ní vytvořte attached vlastnost TextBoxChangedAction.
Do složky Behaviors přidejte abstraktní třídu TextChangedBehaviorBase, která bude fungovat jako základ pro dva podobné objekty Behavior určené pro TextBox (TextBoxTextChangedUpdateSourceBehavior) a PasswordBox (PasswordTextChangedBehavior). Do projektu musíte přidat referenci na knihovnu System.Windows.Interactivity, kterou naleznete většinou ve složce c:\Program Files\Microsoft SDKs\Expression\Blend\Windows Phone\v7.0\Libraries\System.Windows.Interactivity.dll. Bez této knihovny není dostupná bázová třída Behavior<T>.
Metoda RunTextChangedAction se pro objekt Textbox a PasswordBox asociovaný s tímto objektem Behavior pokusí dohledat a spustit delegáta v “attached” vlastnost TextBoxChangedAction. Metoda UpdateSource požádá odvozené třídy o vydání "objektu BindingExpression” voláním metody GetBindingExpression. Vrácený objekt “BindingExpression“ by měl zapouzdřovat propojení vlastnosti view modelu (např UserName) s textem v textovém poli. Po kontrole, že se jedná o oboustranný (two way) binding, metoda UpdateSource zavolá na objektu BindingExpression UpdateSource, což způsobí přenesení hodnoty zadané uživatelem v textovém poli ve view do zdrojové vlastnosti (např. UserName) ve view modelu.
Potomek TextBoxTextChangedUpdateSourceBehavior v metodě GetBindingExpression vrátí BindingExpression pro svou vlastnost Text. V přepsané metodě OnAttached, která je volána vždy, když je objekt Behavior asociován s textboxem, si přihlásíme odběr události TextChanged TextBoxu a při každé změně textu voláme zděděné a výše popsané metody UpdateSource a RunTextChangedAction. V metodě OnDetaching si nezapomeneme odběr události TextChanged odhlásit.
Pro PasswordBox máme dalšího potomka PasswordTextChangedBehavior, který se od třídy TextBoxTextChangedUpdateSourceBehavior liší jen tím, že v metodě GetBindingExpression vrací “BindingExpression” pro vlastnost Password a zadávaný text sleduje přes událost PasswordChanged.
V LoginView si nezapomeňte zkontrolovat, že máte namapovány správně xml prefixy behaviors a i na správné jmenné prostory v C#, abyste mohli využívat novou attached vlastnost TextBoxChangedAction a objekty TextBoxTextChangedUpdateSourceBehavior a PasswordTextChangedBehavior.
prostoryxmlns:behaviors="clr-namespace:RStein.PosterousReader.WP.Behaviors"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Dodejme, že náš delegát TextChangedAction ve view modelu simuluje změnu objektu LoginCommand, který je přidružen k tlačítku Přihlásit se a tlačítko Přihlásit tedy po každém zadání znaku zjistí, jestli má být dostupné. Zopakujme, že vlastnost IsEnabled tlačítka je “nabindována” na vlastnost LoginCommand.CanExecuteCommand.
Aplikaci můžete spustit.
Poznámka: Ještě si ale nezapomeňte do složky UIServices dát kód rozhraní IViewModelResolver a třídy ViewModelResolver, které jsem popisoval v článku propojení view modelu s view (stránkou), protože bez třídy ViewModelResolver by aplikace nebyla schopna pro naše MainLoginView a LoginView dohledat právě vytvořené view modely. A v aplikaci musíte mít samozřejmě všechny další třídy z předchozích článků, které jsou odpovědné za “tombstoning” apod.
U aplikace si můžete zkontrolovat, že:
- Přihlašovací dialog plní všechny požadavky, které jsme vypsal na začátku článku.
- Nemusíme se nijak starat o “tombstoning” aplikace. Zmáčkněte tlačítko Win, poté se tlačítkem Back vraťte do aplikace a všechny hodnoty v textových polích zůstanou zachovány.
- Tlačítko pro přihlášení je dostupné jen tehdy, když textová pole pro zadání jména a hesla jsou vyplněna. Pokud alespoň jedno pole vyplněno není, tlačítko je neaktivní.
- I naše minimalistická podpora rozhraní ICommand vede k tomu, že ve View nemáme žádný “code behind”. Logika stojící za view je jen ve view modelech a XAML tahá data z view modelů přes “data binding”. Pokud přesně tohle to považujete za důležité, budete určitě nadšeni. Jak uvidíte v dalších článcích, já jsem vůči strategii “vše do XAMLu” hodně skeptický, ale XAML puristé
si mohou jít po dnešku jistě ožrat držku slavit.
- I když šlo jen o přihlašovací formulář, vytvořili jsme si další skládací kostky (attached vlastnosti, Behavior, PosterousViewModelBase), které se nám budou hodit při psani dalších view a view modelů (nejen) v této aplikaci.
Zauvažujte nad tím, jestli by se nám také nehodila nějaká podpora ve view modelech pro ukládání nejen tranzientního stavu, ale i pro ukládání “trvalejšího” stavu, který bude dostupný i v další nezávislé instanci aplikace. Možná by stálo za to, aby si aplikace pamatovala poslední zadané přihlašovací jméno nejen při “tombstoningu”, ale aby přihlašovací jméno bylo nabídnuto i při novém spuštění aplikace. A z hlediska vývojáře bezbolestná podpora pro ukládání perzistentního stavu (data s “delší záruční lhůtou”) bude námětem dalšího WP7 intermezza. Doufám, že se těšíte.
Předchozí články:
Tipy pro Windows Phone 7 aplikace I
Tipy pro Windows Phone 7 aplikace II – podpora životního cyklu aplikace (včetně tombstoningu) ve "view modelech”
Tipy pro Windows Phone 7 aplikace III–propojení view modelu s view (stránkou)
Tipy pro Windows Phone 7 aplikace IV - intermezzo I - zjednodušená registrace serializovatelných tříd nesoucích tranzientní stav v KnownTypesDictionary
Monday, 24 January 2011 17:48:26 (Central Europe Standard Time, UTC+01:00)
C# Posterous API | Compact .Net Framework | Návrhové vzory | Silverlight | WP7