Portable
Library Tools sú nástroje pre Visual Studio, ktoré umožňujú vytvoriť
jednu .NET knižnicu ktorá bude podporovať viacero platforiem: .NET
4 Framework, Silverlight 4, Windows Phone 7 a XNA hry. Doteraz bolo potrebné
mať jeden spoločný projekt so zdrojovými súbormi a pre každú cieľovú
platformu vytvoriť zvlášť projekt a do neho pomocou linkovania pridať
zdrojáky zo zdieľaného projektu. Takto bolo možné vyriešiť portovanie
zdieľaného kódu existujúcej Silverlight 4 aplikácie do Phone
7 aplikácie. Lenže toto riešenie má veľké problémy so spravovaním
súborov – všetky súbory trebalo ručne synchronizovať, jeden súbor sa
dá vo Visual Studio otvoriť iba jeden krát, ďalší pokus o otvorenie
skončí chybovou hláškou, ktorú treba odklikávať a tak podobne. Hľadanie
správnych API, ktoré fungujú na všetkých potrebných platformách, tiež
nebolo jednoduché. Veľmi ľahko sa programátor mohol odviazať a použiť
niečo, čo zrazu v inom projekte spôsobilo kompilačnú chybu, alebo
závislosť na inej DLLke (niektoré triedy z .NET 4 sú v Silverlighte
4 v inej fyzickej DLL a tak istom na Phone 7 sa nachádzajú v inej DLL).
Portable Library Tools tak prinášajú obrovské zjednodušenie písania
knižnice, ktorú plánujeme použiť v aplikáciách pre rôzne platformy.
Pri vývoji Windows Phone 7 aplikácie, ktorá bude používať FogBugz API,
som najprv mal vytvorený Phone 7 projekt, do ktorého som prepisoval moju
existujúcu implementáciu klienta pre FogBugz API. Tento klient bol obrovský,
napísaný cez WCF a so synchrónnymi volaniami. Pre Phone 7 bolo potrebné
kompletne prepísať veľkú čast kódu, pretože tak ako Silverlight, aj Phone
7 API obsahuje iba asynchrónne volania pri prácii s I/O a sieťou. Mohol
som sa zároveň zbaviť závislosti na WCF a kód poriadne zoštíhliť. Keď
som mal Phone 7 knižnicu hotovú, tak vyšla finálna verzia Portable Library
Tools nástrojov a rozhodol som sa, že projekt zmením tak, aby som ho
v budúcnosti mohol použiť kdekoľvek. To so sebou prinieslo niekoľko zmien
v kóde, ktoré nie sú na prvý pohľad jasné a vyžadovali si trochu
googlenia.
Boolean.ToString()
Jedna pomocná metóda používala volanie
Boolean.ToString(CultureInfo.InvariantCulture);
, ktoré nie je
v PLT podporované. InvariantCulture som použil z „best-practices“
dôvodov. Boolean hodnotu som formátoval do query stringu a POST dát, takže
som použil InvariantCulture, ktorá zabezpečí, že primitívny dátový typ
bude naformátovaný do takéto textového formátu, ktorý bude bez problémov
spracovateľný inou aplikáciou. PTL však podporuje iba
Boolean.ToString()
metódu. Podľa dokumentácie pre Boolean
preťaženie metódy, ktoré som ja volal, úplne ignoruje parameter
IFormatProvider a teda je jednoduché opraviť volanie na jednoduché ToString()
bez zmeny chovania kódu.
PS: Pôvodná idea za použitím CultureInfo.InvariantCulture bola, aby som
vždy dostal iba „true“ alebo „false“ stringy. Lokalizované .NETy
dokážu vracať texty ako „Pravda“ a „Nepravda“. A samozrejme FogBugz
server nedokáže spracovať takýto lokalizovaný text. Nemám však
momentálne dôkaz, že Boolean.ToString() naozaj vráti lokalizovaný text
v niektorých špeciálnych prípadoch a MSDN dokumentácia v iných jazykoch
hovorí o „true“ a „false“ stringoch. Predpokladám teda, že o tento
potencionálny bug sa vôbec nemusím starať, lebo neexistuje 🙂
Uri.EscapeUriString(string) a Uri.EscapeDataString(string)
Ako webový vývojár som zvyknutý používať triedu
HttpUtility
na zakódovanie textu do správne formátu, ak má byť
text súčasťou URL adresy, HTML obsahu alebo atribútu. Táto trieda je však
z knižnice System.Web.dll ktorá je dosť veľká a nie je vhodná ani pre
desktopové aplikácie, a už vôbec nie pre Silverlight či Windows Phone.
HttpUtility trieda je v týchto dvoch platformách ale prítomná
v System.Windows.Browser.dll knižnici. V Portable Library Tools ju však
nenájdeme a potrebujeme náhradu. Existujú však nové metódy v triede
Uri
, ktoré boli pridané v .NET 3.5 SP1, ktoré rátajú s tým,
že vývojári často pracujú s HTTP protokolom aj mimo webových aplikácií.
Máme teda k dispozícii dve nové metódy:
Uri.EscapeUriString(string)
a
Uri.EscapeDataString(string)
ktoré sú súčasťou základnej
systémovej knižnice a umožnia nám správne kódovať URL/URI adresy, query
stringy a POST dáta.
Uri.EscapeUriString(string)
metóda slúži pre prácu so
stringami, ktoré budú súčasťou URL/URI adresy – napr. cesta k súboru
na serveri a query string dáta.
- fullServiceUrl.Query = "cmd=" + HttpUtility.UrlEncode(command);
+ fullServiceUrl.Query = "cmd=" + Uri.EscapeUriString(command);
Uri.EscapeDataString(string)
metóda zase
zakóduje správne stringy, ktoré budú súčasťou POST údajov.
StreamWriter writer = new StreamWriter(webRequest.EndGetRequestStream(asyncResult);
- string name = HttpUtility.UrlEncode(param.Key);
- string value = HttpUtility.UrlEncode(param.Value);
+ string name = Uri.EscapeDataString(param.Key);
+ string value = Uri.EscapeDataString(param.Value);
writer.Write(name + "="+ value + "&");
SynchronizationContext
Všetky API pre prácu s I/O sú v Silverlighte a na Windows Phone
výhradne asynchrónne. Treba si preto uvedomiť, že kód aplikácia si
vyžiada údaje zo serveru a tieto údaje dojdú späť asynchrónne –
v inom threade. Ak teda používateľ klikne na tlačidlo „Načítaj údaje
so serveru“ a handler metóda zavolá WebClient.DownloadStringAsync(), tak
metóda okmažite skončí a grafické rozhranie aplikácie nezamrzne, lebo
nečaká, kým sa stiahnu údaje zo serveru. Namiesto toho treba počkať, kým
sa nezavolá ďalší handler, ktorý už dostane stiahnuté údaje. Toto sa
však deje v samostatnom vlastné, ktorý je iný od UI vlákna. Načítané
údaje teda môžu byť spracované a grafické rozhranie aplikácie stále
nemusí byť blokované. Až po spracovaní môžeme aktualizovať UI – napr.
zobraziť stiahnutý text. Toto však nemôžeme urobiť kedykoľvek. UI sa
vykresluje vo svojom vlastnom vlákne a preto musíme vyslať požiadavku
o zmenu UI pomocou triedy Dispatcher
. Dispatcher je špeciálna
trieda, ktorá vie, ako sa vykresľujú WPF, Silverlight a Phone 7 aplikácie a
nie je dostupná v Portable Library Tools. Pretože PLT knižnica môže
bežať v rôznych prostrediach, treba použiť namiesto špeciálneho
Dispatcheru všeobecnú triedu, ktorá zabezpečí správnu synchronizáciu
kódu, ktorý potrebujeme vykonať. Takouto triedou je
SynchronizationContext
s dvoma metódami: Post()
a
Send()
. Ide o asynchrónny a synchrónny variant spustenia kódu
v správnom vlákne, ktoré môže upravovať aj grafické rozhranie. PLT
knižnica získa aktuálny synchronizačný kontext pomocou vlastnosti
SynchronizationContext.Current
a podľa toho, či je aplikácia
napísaná pre Win Forms, WPF, Silverlight či Phone 7, tak sa použije správny
synchronizačný kontext na vykonanie kódu.
Portable Library Tools sú zaujímavá vec, pokiaľ potrebujete vyvinúť
aplikáciu pre rôzne .NET platformy a použiť v nich spoločný zdieľaný
kód. Samozrejme treba mať namysli, že PLT sprístupnia v projekte iba tie
API, ktoré sú dostupné na všetkých platformách, pre ktoré je knižnica
vytvárané a z počiatku to môže byť dosť obmedzujúce. Mnohé veci sa
však dajú napísať v .NETe veľmi univerzálne, avšak treba použiť
správnu sadu API. Toto bol hlavne prípad odstránenia HttpUtility, na ktorú
som ako webový vývojár veľmi zvyknutý. .NET sa vyvíja a niekedy je
ťažké udržať krok so všetkými novými a lepšími API. Na druhej strane
táto evolúcia .NETu so sebou prináša aj duplicitu API a treba sa vedieť
správne zorientovať, ktoré triedy a metódy použiť v danom projekte.