Programátor Ali Baba udílí rady do života.

Úterý, červen 29, 2010

Tento blog byl přesunut


Tento blog je nyní umístěn na adrese http://dkolman.blogspot.com/.
Během 30 sekund budete automaticky přesměrováni, případně můžete kliknout sem.

Pokud tento zdroj odebíráte, aktualizujte své odběry na stránce
http://dkolman.blogspot.com/feeds/posts/default.

Neděle, únor 21, 2010

Innovator of the Year

Nedá mně to, musím nás pochlubit: Produkt, na kterém náš tým pracuje, vyhrál cenu "Innovator of the Year" kterou každoročně udílí Xplor International. Více úryvek z tiskové zprávy:


Xplor's Innovator of the Year Award recognizes individual, company or an organization that has conceived and developed an original concept leading to a significant advancement in the industry. The "Innovator" has advanced a new program, product or technology that notably enhances the capabilities of electronic document systems. This year, GMC's recently announced PortalBuilder software solution was recognized for its ability to simplify the process and open doors for marketing firms, print service providers and enterprises to implement timesaving, web-enabled storefronts for variable data documents and integrated personalized multi-channel campaigns. As a result, enterprises gain more flexibility and faster results by having easier access to all the variable components they need and users can enjoy centralized brand management and messaging as well as cost control with decentralized creation, order entry and output.


Je to celkem príma ocenění, před námi tuto cenu dostal např. Xerox, IBM nebo Adobe. Cena bude oficiálně prezentována na konferenci The 2010 Global Document Conference & Vendor Forum 17. března na Floridě, kdyby jste měl někdo náhodou cestu kolem;)

Pondělí, únor 01, 2010

Připojte se k nám

Náš tým hledá novou programátorskou posilu! Pokud umíte C#, baví vás dělat webové aplikace a chcete pracovat pro nejlepší IT firmu v Hradci Králové a okolí, neváhejte a pošlete nám životopis. Oficiální inzerát naleznete zde.

Nemá cenu abych opakoval dlouhý seznam benefitů, které náš chlebodárce poskytuje svým zaměstnancům. To si přečtete v inzerátu a určitě to ještě uslyšíte u přijímacího pohovoru. To hlavní totiž je, že práce u nás je hodně zajímavá. Používáme nové kůl technologie, děláme zajímavé aplikace a máme klienty po celém světě. Přesto, že jsme už celkem velká firma, ceníme si osobní invence, takže pokud přijdete s něčím novým a zajímavým, máte solidní šanci to prosadit.

Vytváříme převážně webové aplikace v ASP.NET MVC, ale máme v portfoliu i desktop klienty napsané ve WPF, služby komunikující pomocí WCF a další hračky. Kouzla na webu děláme pomocí jQuery a jako ORM vrstvu používáme NHibernate. Celé to pácháme ve Visual Studiu za vydatné pomoci ReSharperu a VisualSVN. Dáváme si záležet na architektuře, učíme se být test-driven a pomalu se zlepšujeme v praktikách Scrumu. Za samozřejmost považujeme kolektivní vlastnictví kódu v Subversion a průběžnou integraci s CruiseControl. Snažíme se navzájem učit pomocí párového programování a code review.

A hlavně, je s náma strašná sranda! Hlavně s Radkem. Ale to uvidíte sami na pravidelných týmových a firemních akcích, nebo když zapomenete zamknout klávesnici a odejdete z kanceláře.

Štítky:

Středa, leden 06, 2010

Spike aneb ověření, jestli to vůbec půjde

Po instalaci Linuxu jsem potřeboval vyzkoušet, zda fungují klíčové technologie, na které se bude můj projekt spoléhat. Při té příležitosti si také trochu ošahám MonoDevelop (a zjistím jestli je vůbec použitelné). V programátorštině se tomu řiká spike, neboli malý prográmek, který jako tenký hřebík projde všechny vrstvy aplikace a ověří, že navržené řešení není totální pitomost. Po dokončení se takový projekt nemilosrdně smaže, protože při jeho tvorbě nejsou uplatňovány principy trvale udržitelného programování.

Můj blog poběží na ASP.NET MVC a data chci ukládat do databáze. Mám totiž zaplacený hosting a nechce se mi ho měnit, navíc C# je jediný jazyk, který pořádně ovládám:-) Jako datovou vrstu chci použít Castle ActiveRecord, což je vlastně sugar-added vrstva nad NHibernate. Pro testování bych chtěl použít buď XUnit nebo NUnit.

Vytvořil jsem proto v MonoDevelop solution, do které jsem přidal tři projekty. Jeden bude obsahovat doménové objekty odvozené z ActiveRecordBase<T>, druhý NUnit testy pro ověření základních operací s databází, a třetí bude MVC projekt. V prvním projektu jsem založil dvě jednoduché třídy, Post a Comment. Díky ActiveRecord stačí jejich property okrášlit patřičnými atributy a ORM vrstva je hotová:



Pro ukládání dat budu potřebovat nějakou funkční databázi. Na mém hostingu mám zaplacenou MS SQL databázi, která se na Linuxu nedá použít. Budu proto používat MySQL. NHibernate by měl zajistit, že můj blog bude fungovat na obojím. Protože tomu nevěřím úplně na 100%, budu potřebovat sadu testů, které lze spustit jak na Linuxu, tak na Windows. Po dokončení nové featury na Linuxu spustím sadu testů na Windows a teprv poté publikuju novou verzi na svůj "produkční server".

Nainstaloval jsem proto MySQL, což je na Linuxu otázkou vteřin (záleží na rychlosti připojení) a nakonfiguroval z konzole podle návodu. Vytvořil jsem si pomocnou třídu, která inicializuje ActiveRecord podle konfiguračního souboru a nainstaluje prázdnou databázi:



V testovacím projektu jsem vytvořil jednoduchý test, který v [SetUp] metodě zavolá obě výše uvedené metody. Není to ideální ale pro účely spike to bohatě stačí. Dále jsem přidal app.config pro nastavení NHibernate:



Nevím jestli je to v MonoDevelop bug nebo featura, ale po přidání app.config do projektu není automaticky zajištěno, že se správně nakopíruje do výsledné složky. I když jsem vytvořil nový soubor pomocí šablony "Application Configuration File", bylo nutné ručně nastavit "Build action" na "Application definition" a "Copy to output directory" na "Copy if newer". OK, s tím jsem schopný žít.

Aby se NHibernate byl schopen připojit k MySQL, je potřeba stáhnout MySQL Connector, což je vlastně ADO.NET driver pro MySQL databázi. Zde se objevilo první problematické místo: Když se NHibernate pokoušel načíst assembly MySql.Data, došlo k výjimce. Mono nemohlo assembly najít, i když soubor existoval. Vzpomněl jsem si že názvy souborů v Linuxu jsou case-sensitive a stažený soubor se jmenuje mysql.data.dll, jenže správný název assembly je MySql.Data. Mono proto hledá MySql.Data.dll, které nenajde. Naštěstí stačilo tuto knihovnu přejmenovat a problém byl odstraněn.

Pak už vše jelo jak po másle: Vytvořil jsem několik testů, které vytvářejí Post, přidávají k němu Comment, listují Posty atd. Všechno co jsem zkoušel fungovalo tak jak má. Horší je to ale s vývojovým prostředím. Když jste zvyklí na Visual Studio s ReSharperem, budete v MonoDevelop postrádat online kontrolu chyb, formátování kódu (funguje částečně), přidávání using namespace a referencí pomocí klávesových zkratek, a doplňování vám bude připadat hloupé. Například když píšete attribut, nenabídne vám property která je definovaná v base třídě, takže např. u [HasMany] se nenabízí Lazy=true, protože tato property je definována ve třídě RelationAttribute, která je předkem HasManyAttribute. Nejvíc mně ale vadí spouštění unit testů. Aby jste mohli spustit jeden konkrétní unit test v debugu, musíte nejdřív spustit všechny testy, pak ho nalézt v okně Unit Tests a v kontextovém menu zvolit Run Test With - Mono Soft Debugger. Zatím jsem nepřišel na žádnou klávesovou zkratku, která by test spustila přímo.

Ještě zbývá zkusit napojení ASP.NET MVC na doménovou vrstvu a pak se už můžeme pustit do vývoje skutečné aplikace.

Štítky: , ,

Neděle, prosinec 27, 2009

Jak jsem přešel na Linux a MonoDevelop

Má trpělivost se smrtící kombinací Vista+Visual Studio+ReSharper byla vyčerpána. Mám pocit, že neustále čekám alespoň na jeden prvek z této trojice. Můj upgrade na Windows7 je přitom minimálně tři měsíce daleko, a navíc si nejsem úplně jistý, že to přinese zásadní zrychlení. Rozhodl jsem se proto vyzkoušet Linux+MonoDevelop, pro začátek na nějakém malém testovacím projektíku.

Pro člověka odkojeného Windows je těžké se v bziliónu Linuxových distribucí a balíčkovacích systémů vyznat. Navíc nemám žádný hardware, který bych mohl obětovat na pokusy, takže potřebuju bootovat z 4GB USB flašky, kam se musí vejít vše. Instalace musí být persistentní, abych při rebootu nepřišel o data. Později jsem zjistil, že také potřebuju, abych mohl bez problémů (a bez kompilace jádra:-) nainstalovat nejnovější verzi Mono a MonoDevelop, to totiž na Linuxu není samozřejmost. O tom za chvíli.

Nejdříve jsem si pohrával s Live CD ze kterých jsem vytvořil persistentní Live USB flašku. Zkoušel jsem Slax, než mi došlo že na KDE asi MonoDevelop nepoběží. Pak jsem si hrál s Ubuntu a openSUSE, ale stále to nebylo ono. Live CD openSUSE vytvořené podle tohoto návodu z neznámých příčin záhadně zamrzalo. Ubuntu běželo bez problémů, jenže jsem s překvapením zjistil, že na něj nenainstaluju MonoDevelop 2.2, protože balíčky nejsou aktuální. Rozhodl jsem se proto vrátit k openSUSE, pro které poskytují zkompilované balíčky přímo tvůrci MonoDevelop. Systém jsem tentokrát nainstaloval přímo na USB flašku, jako kdyby to byl hard disk. Tady je postup:

  1. Pro jistotu jsem z notebooku vyndal hard disk, abych ho omylem nesmazal.

  2. Nabootoval jsem z openSUSE Live CD (verze 11.2 GNOME x86_64) a zvolil instalaci.

  3. V konfiguraci disk partition jsem smazal swap a ponechal jen jednu linuxovou partition. Momentálně swap nepotřebuju, protože Linux využívá zatím pouze 600MB paměti, i když používám tři desktopy a Firefox. Až budu swap potřebovat, stejně ho kvůli opotřebení nebudu chtít mít na flašce, ale na hard disku.

  4. Nainstaloval jsem systém a provedl aktualizace přes GUI nástroj. V tuto chvíli už na flašce nezbývalo moc volného místa, tak jsem odinstaloval hry, cizojazyčné lokalizace a OpenOffice. Díky tomu jsem získal kompletně nainstalovaný systém a 1GB volného místa.

  5. Do Package Repositories jsem přidal Mono (http://ftp.novell.com/pub/mono/download-stable/openSUSE_11.2) a MonoDevelop (http://download.opensuse.org/repositories/Mono/openSUSE_11.2/)

  6. Nainstaloval jsem balíček mono-complete s aktuální verzí Mono 2.6.1. Při tom jsem musel změnit providera všech ostatních mono balíčků (protože mono už je na openSUSE nainstalované, ale z jiného zdroje). Balíčkovací mechanismus mi přijde docela robustní, sám hledá závislosti a hlídá jejich porušení, takže změnu providera vám nabídne automaticky.

  7. Nakonec jsem přidal balíček monodevelop verze 2.2.


Příjemné zjištění bylo, že MonoDevelop už v sobě obsahuje podporu ASP.NET MVC, takže stačilo založit novou solution podle šablony a Hello World byl hotový. Podobně jako VS má MonoDevelop ořezaný vývojářský web server, takže stačilo zmáčknout F5 a projekt se spustil.

Musím přiznat, že jsem si myslel že to bude jednodušší. Zvlášť úvodní tápání mezi distribucemi bylo dost zdlouhavé a chvílema jsem už začal pochybovat, jestli je možné to všechno na flašku dostat. Je to samozřejmě tím že jsem linux-lama, a doufám že se to bude časem zlepšovat;) Na druhou stranu, teď mám (skoro) kompletní vývojářské prostředí a můžu se konečně pustit do předělání tohoto blogu, jak už několik let plánuju.

Nyní přichází nejdůležitější rozhodnutí ze všech: Jak se bude můj blogovací engine jmenovat?

Štítky: ,

Čtvrtek, listopad 26, 2009

...a jeden navrch

Kolega mi včera na chvíli půjčil ještě úplně nové HTC HD2. No a musím říct, že je to povedený kousek.



Je sice větší než iPhone, takže ho budete dost těžko strkat do kapsy, ale má zase větší displej a škvíru reproduktoru (telefonovat jsem tím nezkoušel) (používá vůbec někdo smartphone k telefonování?). HTC je zhruba stejně placatý a těžký jako iPhone.

Stejně jako iPhone se ovládá prsty a zvládá multitouch. Je to moc pěkná hračka, na UI si někdo pěkně vyhrál. Mobilní Opera zvládá bez problémů běžný (nemobilní) web, na rozdíl od Internet Exploreru, který je stále 100 let za opicema (sorry, opice). Operační systém v telefonu je přitom Windows Mobile, což má pro nás .NET developery jisté kouzlo. Ještě že musím koupit zimní pneumatiky a nemusím řešit dilema typu iPhone vs. HTC HD2.

Pondělí, listopad 23, 2009

Čtyři smartfouni

Leží mi na stole čtyři smartfouni. Díky tomu, že děláme mobilní verzi našeho produktu, máme příležitost vyzkoušet, jak vypadá web v různých telefonech, a taky si je pěkně ošahat. Máme tu iPhone 3GS, T-Mobile G1, Sony Xperia X1 a BlackBerry 9000.



Rozhodně si netroufám na komplexní a nezaujatou recenzi, ale říkal jsem si že by bylo dobré napsat své první dojmy z těchto čtyřech smartphones, jak působí na laika. Jsem sice zkušený uživatel PC, ale telefonuju z levného vysouváku Samsung (to je ta lednička, smějou se mi kolegové), a smartphone jsem doteď v podstatě nedržel v ruce.



iPhone je fakt sexy telefon a má jednoduché ovládání. Navíc můžete vyměnit sim kartu bez vypnutí telefonu, což při takovémto testování oceníte. U ostatních je nutné odstranit zadní kryt a vyndat baterku. Jako jediný se ho povedlo připojit na firemní wi-fi. Má největší displej a je nejtenčí.



T-Mobile G je ne zcela podařený pokus o iPhone. Má sice dotykové ovládání, ale zvětšovat/zmenšovat musíte tlačítky (dvěma prsty jako u iPhone to nejde). Na druhou stranu můžete po odkazech na webové stránce skákat kuličkou, což je dost fajn. Při dobíjení baterky se zadní strana zahřeje tak, že by šel použít i jako ponorný ohřívač.



BlackBerry bych se styděl vytáhnout na veřejnosti, okamžitě se prozradí že pracujete v IT. Při zadávání URL do browseru nejde zadat tečka jednoduše, musíte podržet Alt. Nemá to dotykovej displej, a ovládat kurzor kuličkou je šílenost, neskáčete po odkazech, ale posunujete kurzor po obrazovce, takže furt prstíte jak vzteklí. Prohlížeč je dost ořezanej, nezvládá moc ani CSS ani javascript. Ze všech čtyřech telefonů bylo na BlackBerry načítání stránky nejpomalejší.



Sony Xperia je tlustá cihla, která se ovládá tenkou tyčkou, musíte se trefit do titěrných čudlíků, a nedokážu si představit jak bych to zvládal v metru nebo ve vlaku. Tyčka se strká shora do telefonu, a asi bych ji hned druhý den ztratil. Na druhou stranu, furt lepší a rychlejší než než klávesnice BlackBerry, ale já jsem myšoidní typ. Windows Mobile asi nebude můj oblíbený systém. Hlášky jako "systém čeká", když ve skutečnosti čekám já na systém, nebo výhrůžky jako "neuložená data budou ztracena", mě zvedaj ze židle. Internet Explorer na Xperii nezvládá javascript, Opera sice ano, ale je dost pomalá.

Pokud bych si měl některý z těch telefonů koupit, určitě bych si vybral iPhone. Je to sice nejhorší telefon na trhu, ale z těchto čtyřech na mě udělal nejlepší dojem. Ovládání je intuitivní, a navíc je to vyloženě pěkná hračka.

Pondělí, říjen 26, 2009

Co by měl každý programátor vědět

Občas mě zarazí, že i lidi, které považuju za dobré programátory, neznají základy programovacího jazyka, ve kterém píšou 95% kódu. Například nedávno v naší kanceláři proběhla debata, proč si StyleCop stěžuje na to, že jsou v kódu nadbytečné závorky. Šlo o složenou podmínku v příkazu if, kde si dotyčný pomáhal závorkami, protože si nebyl 100% jistý s prioritou operátorů && a ||. Co na to říct?

Podle mého jsou určité základy, které musí každý programátor o svém jazyce vědět. Priorita operátorů podle mně patří mezi ně. Argument, že v jazyce XX nebo YY to bylo jinak, je podle mně zcestný. Programovací jazyky jsou natolik jednoduché, že programátor nemůže mít problém si těch pár věcí zapamatovat. Netvrdím, že musíte z hlavy vědět vše o nějakém obskurním jazyku, který používáte jednou za uherský rok, ale pokud se někdo už několik let živí psaním kódu v C#, musí takové věci znát bez zaváhání.

Co všechno by jste tedy měli vědět o svém hlavním programovacím jazyku?

  • Priorita operátorů (!, ++, ||, &&, ?:)

  • Jak zapsat hodnotu různých datových typů (0xAF, 123 a 123L, 0.12M a 0.12D)

  • Základní konverze na string a zpět (například donedávna jsem považoval za samozřejmost, že každý ví jak konvertovat "FFEEDD" na int, ale byl jsem vyveden z omylu)

  • Speciální jazykové konstrukce jako anonymní delegáti a lambda funkce



Tento seznam určitě není kompletní, pokud vás napadne něco dalšího, napište to do komentářů.

Pokud tyto základní věci neovládáte, riskujete dva velké problémy: Budete se pomalu orientovat v kódu ("jakého typu je var a = new[] { 1, 10, 100, 1000 };?"), a při psaní budete používat zbytečné ornamenty (if(((age>60)||(disabled==true))&&(balance>limit))). Druhý problém je o dost horší než první, protože při čtení zpomalujete jen sami sebe. Pokud ale kvůli své neznalosti použijete o 8 závorek navíc, ztěžujete práci ostatním, protože znepřehledňujete kód. Je sice pravda, že ostatní pak nemusí používat svn blame, protože už znají váš styl:-) Být poznat podle stylu kódu ale není nic o co by jste měli stát. A taky budete hodně nadávat na StyleCop:-)))

Pokud neovládáte ani naprosto základní věci, jak chcete přesvědčit ostatní o tom, že jste dobří programátoři?

Neděle, srpen 23, 2009

Co všechno musíte vyřešit při psaní WPF aplikace

WPF je kůl, o tom žádná. Proti Windows Forms je to opravdu obrovský skok kupředu. Ale to ještě neznamená, že při tvorbě aplikace založené na WPF nemůžete dostat osypky a že vás nečekají bezesné noci. V tomto příspěvku se pokusím shrnout hlavní problémy, ke kterým je třeba se postavit čelem, bez nároku na nalezení konečného řešení:

  • INotifyPropertyChanged

  • Validace

  • Lokalizace

  • Undo/Redo


INotifyPropertyChanged


Aby View vědělo kdy se má updatovat, je nutné u všech ViewModel objektů implementovat rozhraní INotifyPropertyChanged. Naštěstí je velmi jednoduché, ale budete muset psát spoustu opakovaného kódu:

string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name);
}
}

Vyhnout se mu můžete buď generováním kostry ViewModelu pomocí DSL jazyka, nebo pomocí AOP (kolegové používají PostSharp), kde stejná property může vypadat takto:

[Notify]
public string Name { get; set; }

Lokalizace


Existuje několik přístupů k lokalizaci WPF aplikace, které se liší způsobem uložení resource stringů, možností výběru lokalizovaných vlastností a možností přepínat jazyk "za běhu", bez nutnosti restartu aplikace. Řešení které doporučuje Microsoft (přes binární XAML, neboli BAML) potřebuje externí nástroj LocBaml.exe, editovat jazykové verze musíte v CSV a při přepnutí jazyka za běhu se nelokalizují již otevřená okna. Které vlastnosti budou lokalizované se dá určit až při lokalizaci, takže překladatel se může rozhodnout, že např. sloupec v tabulce bude mít jinou velikost nebo label se zarovná na jinou stranu. To může být výhoda i problém, záleží na vašich potřebách. Další dva způsoby naleznete v článku Localizing WPF Applications using Locbaml, oba ale neumožňují přepínat jazyk za běhu.

Zajímavé řešení je popsáno v WPF Localization - On-the-fly Language Selection. To už umožňuje skutečné přepnutí jazyka bez nutnosti restartu aplikace. Je založené na vlastním Converteru, který provádí lokalizaci, díky čemuž lze používat poziční parametry v resource stringu ("{0}") a jejich hodnoty dokonce bindovat. Trik umožňující okamžité přepnutí jazyka spočívá v nahrazení lokalizovaných hodnot MultiBinding objektem, kde první vazba je na LanguageContext.Instance.Dictionary, která při změně jazyka posílá událost PropertyChanged. To způsobí znovunačtení všech hodnot. Tomuto řešení bych vytknul pouze způsob uložení resourců (vlastní XML), ale velmi lehce si můžete napsat vlastní LanguageDictionary a číst hodnoty např. z resx souborů.

Validace


Při prvním pohledu na WPF se zdá, že validace je vyřešená věc, ale pokud chcete použít vzor MVVM, není to tak jednoduché. Ve ViewModelu tedy implementujeme IDataErrorInfo a projdeme všechny deklarace Binding v XAMLu a přidáme ValidatesOnDataErrors=true.

Jenže kromě business validací je nutné také validovat vstup od uživatele. To jsou opravdu dvě různé věci: Mějme třídu Person s property Age typu int. "Business" validace je, že Age nesmí být záporné a musí být menší než 150. Když je hodnota mimo tento rozsah, vrátíme přes IDataErrorInfo chybovou hlášku. Jenže co když uživatel zadá "qweqweqwr"? Výchozí chování Bindingu je výjimku prostě spolknout. Můžeme nastavit ValidatesOnExceptions na true, ale jednak nemůžeme změnit validační hlášku, a také nechceme žádné výjimky. Výjimky jsou pro výjimečné případy, např. když je vyhlášen výjimečný stav. Spoléhání na výjimky může aplikaci znatelně přibrzdit.

WPF Binding zpracovává vstup přes řetěz validátor - konvertér - property setter. Možným řešením by bylo vkládat vlastní validátory do deklarace Bindingů. To ale odporuje našemu cíli, dostat všechnu logiku do ViewModelu. Navíc až budeme objekt ukládat (uživatel zmáčkne Ctrl+S), nedozvěděli bychom se, že na formuláři jsou nevalidní data. Informaci o validitě si přece chceme přečíst z ViewModelu. Nejen kvůli tomu odpadá i další možnost: generovat validátory podle metadat ViewModelu. Ani to nezkoušejte, nejde to. Jak zjistil Miro, Binding je jen MarkupExtension, který vytvoří BindingExpression, který se teprve stará o update hodnot, jenže většina jeho důležitých vlastností je internal.

Jediným mně známým řešením je proto deklarovat ve ViewModelu property Age jako string a provádět konverzi až tam. Když se konverze povede, zapíšete property do Modelu, kde už je typu int a kde se provede business validace. Je to víc práce ale zase má ViewModel informaci o tom, jestli je formulář validní. Implementace je popsaná v článku Using a ViewModel to Provide Meaningful Validation Error Messages.

Aby to nebylo tak lehké, představte si že chcete z ViewModelu vrátit hlášku "Věk musí být mezi {0} a {1}". Obstarožní interface IDataErrorInfo umožňuje vracet pouze string, takže ve ViewModelu provedeme String.Format a vrátíme už hotový text. Jenže to komplikuje lokalizaci. Kvůli přepnutí jazyka za běhu bychom potřebovali vracet klíč do resourců a pole parametrů, aby lokalizátor mohl aktualizovat hlášku. Ideální by tedy bylo napsat si vlastní interface, lepší a hezčí než IDataErrorInfo (to by věru nebylo těžké), protože do ValidationError objektu můžeme ukládat daleko více informací než jen string. Pomocí Reflectoru kolega Miro zjistil, že Binding při nastavení ValidatesOnDataErrors=true vkládá do pole validátorů objekt DataErrorValidationRule. Kdybychom tedy napsali vlastní ValidationRule, mohli bychom přečíst errory pomocí vlastního interface a vytvořit takový ValidationError objekt, který by byl lokalizátor schopen za běhu překládat. Jenže opět narážíme na to, že property BindingExpression.ItemSource, která obsahuje zdroj data bindingu (ViewModel), je internal! Zbývá nám tedy buď oželit okamžitou lokalizaci validačních hlášek, přinutit všechny BindingExpression objekty provést refresh, nebo si do stringu serializovat template s parametry a v lokalizátoru je deserializovat, vše mi přijde ošklivé.

Undo/Redo


WPF má sice vestavěnou podporu pro undo/redo v TextBoxu, ale to nám samozřejmě nestačí, potřebujeme undo/redo v celé aplikaci (nebo lépe, v celém editovaném projektu, který tvoří undo scope - projektů můžeme mít otevřených víc naráz). Zde se nabízí klasické řešení pomocí UndoItem objektu a undo a redo stacku, popsané třeba zde. Je ovšem nutné vyřešit otázku jak nalézt undo scope (projekt), když naše ViewModel objekty tvoří košatě rozvětvený strom.

Také musíme implementovat undo transakce. Kvůli tomu, aby bylo uživatelské rozhraní živější, jsme totiž ve všech Binding deklaracích nastavili UpdateSourceTrigger=PropertyChanged. Díky tomu je uživatelův vstup okamžitě validován, ale protože se přes ViewModel posílá každý jednotlivý znak který uživatel zadá do TextBoxu, bylo by undo dost nepoužitelné. Potřebujeme tedy zahájit undo transakci při vstupu do TextBoxu a ukončit ji při jeho opuštění (nebo stisku enter). To nás nejen odsuzuje k handlování událostí nebo vlastnímu TextBoxu, ale má to i jeden háček: Při kliknutí myší do menu nebo na toolbar TextBox neztratí focus. Tím pádem se při Save/Load neukončí undo transakce a nezahájí se nová, na což je potřeba myslet a modelovat root undo scope (projekt) tak, aby ho bylo možné při Load celý zahodit a vytvořit nový (i pak si ale TextBox drží focus a nezahájí novou transakci).

Závěr


Jak je vidět, komplexitu dobré aplikace není radno podceňovat ani s tak vyspělým frameworkem, jako je WPF. Pokud narazíte na další problémy nebo na jiná řešení, dejte vědět.

Sobota, červen 13, 2009

Architektura Ginger

Ginger je náš framework pro webové aplikace. Tvoří ho knihovny s infrastrukturním kódem a sada doporučení (nebo spíš přikázání:-))), jak rozdělit aplikaci na vrstvy a které projekty umístit do které vrstvy. Je určen pro webovou vícevrstvou škálovatelnou aplikaci, která ukládá data do databáze.

Podporován je jednoduchý deployment na jeden počítač i složitější varianta s webovými servery v DMZ a aplikačními uvnitř sítě; v zásadě jde jen o změnu konfigurace.

Nedávno jsem o tom dělal interní přednášku, ze které pochází prezentace, kterou zde s laskavým svolením mého zaměstnavatele uveřejňuji. Dozvíte se v ní, jak vypadá cílové prostředí, pro které je Ginger určen(a), jak je aplikace rozdělená do vrstev, jak vypadá kód na jednotlivých vrstvách, co v našem případě znamená "M" v ASP.NET MVC a kolik návrhových vzorů z PoEAA používáme (nebo plánujeme používat).

Požadavek na oddělené nasazení webových a aplikačních serverů měl na architekturu Ginger zásadní vliv. Musíme totiž počítat s poměrně pomalým a nespolehlivým síťovým spojením (v porovnání s programováním v jedné aplikační doméně), a proto nemůžeme použít Open Session In View (OSIV) pattern, ani skládání ovládacích prvků ve stránce přes RenderAction. Všechny požadavky na data nutná pro renderování stránky potřebujeme mít už v controlleru, při volání aplikační vrstvy (Remote Facade), to znamená před tím, než se vůbec vytvoří instance View (kdo neví proč, nechť si přečte vynikající knihu Release It!). "M"odelem, který v ASP.NET MVC podstrkujeme View, je v našem případě ResponseMessage, kterou vrací aplikační vrstva. Díky tomu máme zcela oddělený doménový model (kde je aplikační logika) od View, a proto problém zmíněný na diskusi o MVC Best Practices, kdy hrozí že se z View bude měnit doménový model, je nám zcela cizí.

Abychom co nejvíce omezili opakování kódu na různých vrstvách, vytvořili jsme si pomocí DSL Tools modelovací jazyk, ze kterého spoustu kódu automaticky generujeme: SQL create scripts, doménové objekty, repository a query objekty a DTO objekty, které v sobě obsahují metadata, takže z nich je možné za běhu vygenerovat validační javascripty do stránky. Vygenerovaný kód je přitom možné rozšiřovat bez nutnosti úpravy DSL jazyka nebo generovacích šablon, díky partial třídám, nebo volitelně double derived vzoru.



Zde si můžete stáhnout plnou verzi prezentace, se všemi animacemi, které SlideShare neumí zobrazit.