Vítejte na blog.vyvojar.cz Přihlásit | Registrovat | Pomoc

Název tohoto článečku je trošku nejasný, ale osvětlení přijde hned.

Dělám na projektu, kde je v SQL 2008 databázi tabulka obsahující objekt X. Ten může mít klasické hierarchické uspořádání s jiným objektem typu X, čím se vytvoří typický strom. Každý objekt X má jednu vlastnost M, která je daná číselníkem C.

Úkol pro řešení byl na první pohled jednoduchý. Mám jednu instanci X1 objektu typu X, která ale může být v libovolné úrovni stromu. Jak co nejjednodušeji zjistím, které hodnoty číselníku C nejsou použité v žádném objektu typu X, který je ve stejném stromě jak ten X1.

Samozřejmě by se dalo to řešit kódem, ale po krátkém bádání ve schopnostech SQL 2005/8 jsem našel dle mého názoru elegantní řešení, které vyřeší vše v SQL serveru. Využil jsem schpnost rekurzivního prohledávání v SQL bez nutnosti vytvářet temporary table. Odborně to má název CTE, je plno blogů které popisují prohledávání od root elementu směrem dolů (většinou vazbu zaměstnanec - vedoucí)

Já udělal při bádání dvě tabulky, Task se sloupečkem Id, ParentId, Popis a Typ. Pole Typ je id číselníku Type (druhá tabulka). První blok mého SQL kódu hledá root prvek stromu (to jest ten, kde parentId bude NULL) a pak lehce najde vše, co se ve stromě vyskytuje a pomocí NOT IN najde v číselníku jen to, co ve stromě není.

Jen pro zajímavost, zda se ve stromě pohybuji směrem dolů nebo nahoru je dáno pomocí JOIN podmínky v sekcích WITH, podle toho zda z tabulky beru Id nebo ParentId.  

 

DECLARE @root AS INT;

DECLARE @start AS INT;

SET @start = 5;

 

WITH UpCTE

AS

(

      SELECT Id, ParentID, Popis,Typ, 0 as lvl

      FROM Task

      WHERE Id = @start

     

      UNION ALL

      SELECT C.Id, C.ParentID, C.Popis,C.Typ, P.lvl + 1

      FROM UpCTE as P

      JOIN Task AS C ON P.ParentID = C.Id

)

SELECT @root =Id FROM UpCTE

WHERE ParentID IS NULL;

 

WITH SubsCTE

AS

(

      SELECT Id, ParentID, Popis,Typ, 0 as lvl

      FROM Task

      WHERE Id = @root

     

      UNION ALL

      SELECT C.Id, C.ParentID, C.Popis,C.Typ, P.lvl + 1

      FROM SubsCTE as P

      JOIN Task AS C ON C.ParentID = P.Id

)

SELECT * FROM Type

WHERE Id NOT IN (SELECT Typ FROM SubsCTE)

 

Tak změna spočívající v rozdělení TechEdu na DEV a IT týden vydržela v USA, narozdíl od Evropy, pouze jeden rok. Oficiálně je důvodem opětovného spojení do jednoho týdne a BIG eventu feedback od účastníků TechEdu 2008.

Nový společný termín pro TechEd 2009 je 11.-15. května 2009 v LA, California.

Součástí WF je knihovna activities, které můžete používat (záměrně se snažím nepočešťovat activity, aby bylo zřejmé, co mám na mysli). Některé se používají triviálně, jiné v sobě skrývají několik záludností. Jednou z těch záludnějších je Replicator Activity.

Tuto activity použijete, pokud budete potřebovat vykonat nějakou činnost nad kolekcí či polem. Něco jako foreach (velice zjednodušeno). Oproti foreach je tu jeden zásadní rozdíl. Akce můžete pustit buď sekvenčně, nebo paralelně.

Pojďme se podívat na tuto activity podrobněji. Po jejím vložení do workflow je situace následující:

prvni WF

 

Nápisem Drop Activities Here (množné číslo) se nenechte zmást, můžete tam vložit pouze jednu activity. Pokud jich tam potřebujete dát víc, tak je cesta jednoduchá. Nejdřív vložíte Sequence activity nebo jinou kontajnerovou activity a pak už do ní můžete naložit cokoliv potřebujete.

Než se posunu dál, podívejme se na vlastnosti, které Replicator Activity má (seřazeno dle významu):

(name)

Zde je význam doufám jasný

Description

Fantazii se meze nekladou, co sem zadat

Enabled

True/False. Pokud je False, activity se jednoduše přeskočí

UntilCondition

Podmínka, která se bude vyhodnocovat po vykonání child activity. Pokud je výsledek True, dál se nepokračuje. Pozor na okamžik vyhodnocení v závislosti na nastavení vlastnosti ExecutionType. Podmínka může být definovaná buď pomocí metody nebo rule.

ExecutionType

Sequence (default) / Parallel. Určuje, zda se child aktivity bude vykonávat sekvenčně, nebo zda se spustí paralelně. Paralelně ovšem neznamená ve stejném okamžiku ve více vláknech. Nadále platí single-threaded model WF.

InitialChildData

Kolekce nebo pole podporující interface IList. Replikator pomocí tohoto interfacu bude postupně vybírat z kolekce nebo pole jednotlivé prvky a předá je do child activity. Mechanizmus předání bude popsán dále.

Handlery

Initialized

Inicializace samotného replicatoru

Completed

Konec činnosti replicatoru, všechny vnořené activity jsou hotové

ChildInitialized

Start nové child activity. Místo vhodné pro nastavení dat určených ze vstupní kolekce pro danou child activity

ChildCompleted

Jedna z child activit skončila, je na vás, co s tím

 

Pro prvotní test jsem nachystal velice jednoduchou konfiguraci. Replicator Activity obsahuje pouze jednu Code Activity, která na obrazovku vypíše slůvko „Akce". Vlastnost InitialChildData je nabindovaná na kolekci List<int> obsahující čísla 1 až 10. Nic víc pro první pokus nastaveno není.

V tomto případě vypadá výpis programu takto:

Nic překvapujícího. Uděláme malou, na první pohled nijak strašnou změnu. Ať víme, že se něco děje s podmínkou UntilCondition, nastavme ji na metodu, které vypíše nějaký text a VŽDY nastaví e.Result na false.

Pokud je nastaveno ExecutionType na sekvenční, bude výsledek běhu tento:

Co z toho výpisu lze vyčíst:

  • a) Než se spustí poprvé vnořená activity, provede se test na UntilCondition. Pokud by vrátil true, child activity se nespustí ani jednou
  • b) Po ukončení každé child activity se vykoná opět vyhodnocení UntilCondition. Pokud by vrátilo true, další child activity se nespustí.
  • c) POZOR! Neproběhl výpis „Konec workflow..." Není to tím, že by se mi to tam nevešlo, ale tím, že replicator vyhodnotil podmínku UntilCondition jako false, snaží se tedy dál vykonávat vnořený kód, tam ale nemá už z listu co poslat a aplikace je tuhá.

Pro odstranění problému c) je nutno zajistit, že v podmínce otestujete i vlastnost AllChildrenComplete a pokud je true, vždy vrátíte true. Upravená metoda pro UntilCondition může vypadat v mém demo případě takto:

private void UntilPodminka(object sender, ConditionalEventArgs e)

{

     Console.Write("Vyhodnocuji podminku");

     ReplicatorActivity ra = (ReplicatorActivity) sender;

     if (ra.AllChildrenComplete)

         e.Result = true;

     else

         e.Result = false;

}

Teď už je výpis kompletní a vypadá takto:

Co se stane, když změním ExecutionType na Parallel? Zde je vidět situace v tomto jednoduchém případě:

Jak je vidět, byl běh následující:

  • a) Na začátku opět jako první proběhl test UntilCondition
  • b) Pro všechny prvky mé kolekce byla spuštěna child activity. Ještě jednou zdůrazňuji, že i když je ExecutionType Parallel, nejedná se o běh více activities v jednom okamžiku.
  • c) Opět po skončení každé child activity byl spuštěn test na UntilCondition. (Pokud bych ho neměl už opravený, i tady by to vytuhlo)

Varianta, kterou jsem testoval, byla jednoduchá, protože vnořená activity byla pouze code activity a nedostala se nikdy do stavu, že by byla možnost naplánovat běh jiné activity.  Co ale nastane za situace, že běh child activities bude dlouhý a bude se skládat z několika activities. Pak může nastat situace, že některá z prvních activities co skončí, zajistí, že se UntilCondition vyhodnotí jako true. A co pak s těma, co jsou ještě naplánované, že poběží?

Já jsem mírně upravil můj demonstrační příklad.

Sequence Activity jsem musel přidat, aby replikátor mohl replikovat činnost skládající se z tří částí. Akce activity nadále dělá to, co dělala, vypisuje pouze „Akce". Nová activity Hotovo zase vypíše „Hotovo". Delay activity Cekani se nastavuje v handleru události ChildInitialized. Kod je tento:

        private void StartPotomka(object sender, ReplicatorChildEventArgs e)

        {

            SequenceActivity sa = (SequenceActivity)e.Activity;

 

            DelayActivity da = (DelayActivity)sa.Activities[1];

 

            int sec = 2 + (pocitadlo % 3) * 5;

            pocitadlo++;

 

            Console.WriteLine("Nastavuji delay na " + sec.ToString());

            da.TimeoutDuration = new TimeSpan(0, 0, sec);

        }

Pro jednoduchost jsem vynechal řešení možných chyb castingu. Proměnná pocitadlo je privátní členský prvek třídy ReplicatorDemo, která realizuje workflow. Slouží na to, aby přiřadil prvnímu a čtvrtému replikovanému potomku delay na 2 vteřiny, druhému a pátému na 7 vteřin a třetímu na 12 vteřin.

Další počítadlo jsem přidal pro napočítávání počtu průchodů přes UntilCondition. Je nastaveno tak, aby při třetím a dalších průchodem nastavilo výsledek na true.

Dále jsem pro zkrácení výstupu zmenšil kolekci List<int> tak, že obsahuje jen čísla 1 až 5.

Teď se projeví moje změny:

Co je z uvedeného výsledku vidět:

  • a) Před spuštěním první child activity proběhne inicializace všech replikovaných potomků
  • b) Pak proběhnou všechny první activity ze sekvence (5x Akce)
  • c) Delay se postará o to, že první skončí 1. a 4. replikace (Delay byl jen 2 vteřiny) a při vyhodnocení vrátí UntilCondition true
  • d) Všechny neukončené sekvence jsou „odstřeleny" (uvedeny do režimu Cancel) a tím pádem žádná ze zbývajích sekvencí už nenapíše „Hotovo"

Podstatné je si zapamatovat, že jakmile je podmínka UntilConditition true, všechny ještě běžící sekvence se zruší a nedostanou šanci dokončit, co mají rozděláno. Jediná možnost je připravit tzv. Cancel Handler na úrovni sekvenční activity. V něm můžete při předčasném ukončení činnosti provést patřičný úklid. Já tam pro ukázku přidal jen výpis hlášky „Cancel".

Zde je výsledek:

Jak je vidět, v každé ze tří předčasně ukončených sekvencí proběhl Cancel Handler.

Posledním bodem který chci vysvětlit je cesta, jak dostat patřičná data do child activity při jejím spuštění. Řešení vyžaduje trošku víc práce. Jako první krok je nutno vytvořit si vlastní custom activity děděním od např. Sequence Activiy. Jediné, co do té activity přidáte je členský prvek (nejlépe Dependency Property), který bude sloužit k uložení informace specifické pro danou spuštěnou kopii child activity. Pak do Replicator Activity vložíte tuto vlastní activity a připravíte handler pro událost ChildInitialized. V ní pak pomocí vlastnosti InstanceData event argumentu zjistíte, jaká hodnota z kolekce je přiřazená této replikované kopii. Může to vypadat např. takto:

private void StartPotomka(object sender, ReplicatorChildEventArgs e)

{

     ((MojeActivity)e.Activity).MojeData = (int) e.InstanceData;

}

Po této poslední úpravě mi už prográmek pěkně píše i informaci, která child activity právě běží.

Pokud jste se dočetli až sem, poprosím o zaslání Vašeho názoru, zda má smysl připravit další povídání o konkrétních activities a jejich chování. Můžete mi napsat i jiné tipy na to, co by Vás z oblasti WF zajímalo.

Tak a je konec. Včera jsem díky večerní party nic nepsal a tak dnes shrnu to nejzajímavější z posledních dvou dnů.

Z přednášek, které přinesly něco zajímavého nemohu nevzpomenout projekt "index for objects". Jedná se o dílo jednoho člověka, ale otevírá zatím nepopsané možnosti LINQ. Jedná se o doplněk, který přináší indexy do práce s objekty. Pokud se provádělo hledání v 100.000 prvkové kolekci, trval LINQ dotaz s využitím indexu 4 ms, bez něj víc jak 1000 ms. MS v tomto směru zatím nechystá nic podniknout, takže pro nadšené ponocovače se otevírá prostor pro kreativitu. Projekt o kterém je řeč najdete na adrese http://www.codeplex.com/i4o

Další bod, který mě zaujal byla přednáška od softwarového architekta z Kanady. Jmenuje se Mario Cardinal a měl zajímavou přednášku na téma, jak řešit separaci tříd a komponent při návrhu aplikace. Jedna z metodologií, kterou přednesl využívá "Dependency Injection Container". Velice zjednodušeně řečeno, je to kontajner, který obsahuje informace o závislosti mezi objekty a s jeho pomocí se staráte o to, že máte vždy k dispozici správné typy objektů a hlavně je můžete deklarativně zaměnit za něco jiného, pokud bude potřeba (toto se běžně používá u autentizačních providerů). Existuje několik projektů, které tento mechanizmus uvádějí do života. Doporučoval dva open source projekty - NInject a Unity od MS, které je součástí Enterprise Library ver. 4. Naopak varoval před projektem StructureMap, který sice není špatný, ale už rok se na něm viditelně nepracovalo a poslední verze je z 04/2007. Závěrem své přednášky předvedl fungování produktu, který nabízí zajímavé možnosti při vyhodnocování závislostí a různých vlastností projektu. Používá syntaxi jazyka SQL. Licencován je buď na stanici nebo na build server, což většinou bohatě stačí. Cena je 300€ na jeden server. Co to umí se můžete podívat na jejich webu, www.ndepend.com

Poslední den jsem se soustředil hlavně na HOLy a absolvoval jsem jen dvě přednášky. Ale obě stály za to. První popisovala nový WCF adaptér pro BizTalk 2006 R2 a jeho použití. Poslední se týkala WF a řešení komunikace u Long Running Workflow při využití WCF jako komunikačního kanálu mezi WF a klientem.

Závěrem můžu shrnout své pocity z letošního TechEdu asi takto. Rozdělení TechEdu zřejmě pomohlo. Přes 11.000 lidí vloni bylo moc a problém byl s logistikou i prostorem pro řešení speciálních developerských potřeb. Tím, že IT sekce obsadí plochu konferenčního centra až příští týden, byl na konferenci docela příjemný klid a vše šlo jako na drátku. Co se týče témat, neobjevilo se nic speciálně nového, o většině věcí už byla řeč minulý rok, ale nějaké trendy se daly vysledovat.

Zásadní vzkaz souvisí s kódovým jménem "Oslo", o kterém byla řeč na dvou přednáškách. Jedná se o pokračování v sjednocování technologií do jedné rodiny a zde je řeč o sloučení do společného funkčního celku BizTalk, WCF, WF a SQL 2008 pro zajištění komunikace mezi aplikacemi. BizTalk bude zároveň host service pro WF a jeho primární komunikační nástroj bude WCF. Ve znamení podpory WCF bylo přednášek až, až. Naopak o .NET remotingu nepadlo ani slovo a pokud někdo začíná nový projekt, doporučuji v tichosti tuto technologii vyškrtnout ze seznamu. Nikdo z MS to nikde oficiálně neřekne, ale z postoje MS k této technologii se dá lehce vytušit, že je určená na odpis. Neexistuje ani žádný můstek, který by v budoucnu umožnil konverzi .NET remotingu na WCF.

Další produkt, o kterém bylo množství přednášek byl Sharepoint jako sjednocující nástroj pro správu dokumentů. Nakolik je toto téma úplně mimo mých znalostí, snahu MS o rozvoj v tomto směru můžu vytušit pouze z nárůstu prostoru pro Sharepoint (jak v přednáškách, tak plocha pro konzultace) a zvýšení množství firem, které nabízely v partnerské sekci řešení pro Sharepoint a nástroje na správu a programování nad Sharepointem.

Jako třetí zásadní téma se řešily otázky kolem rodiny SQL Serverů 2008, a to od využití verze Micro až po kryptování dat ve velkém SQL 2008. Tyto témata taky přenechám povolanějším.

No a nakonec si neodpustím nerýpnout do obchodníků MS, kteří stanovují cenu za účast na TechEd konferenci. Přijde mi neskutečné, že registrační poplatek na TechEd 2008 v Barceloně je 1950 € + VAT (tuším 16%) a v USA to bylo 1.690 $. Takže za rok na TechEdu 2009 v Los Angeles. Dev týden bude od 12. do 15. května.

Středa na TechEdu se pro mě nesla ve znamení hledání dobré přednášky. Ukázal se klasický problém, že ne vždy je popis a realita shodná.

První přednáškou, kterou jsem chtěl absolvovat, mělo být téma jak vytvářet „Human workflow“ pomocí WF. Jenže když přednášející ze 75 minut zabil prvních 30 výkladem o tom, co je to vlastně State Machine Workflow a dle slidů ho ještě čekal výklad Persistence a Trace Service z WF, pochopil jsem, že o specialitách Human Workflow moc nestihne říct a odfrčel jsem aspoň na druhou polovinu přednášky o tom, jak využívat ASMX/WCF Service Factory při návrhu projektu. Vizualizace návrhu serviců s grafickým definováním metod a data kontraktů služeb vypadalo velice příjemně ovladatelné a generování kódu včetně spousty atributů značně ulehčilo. Víc o této service factory zde.

Další přednáška se týkala programování ve vláknech a nových možností v souvislosti s LINQ. Těm kdo ještě nenarazili na slůvko PLINQ, doporučuji trochu zagooglit a přečíst si něco o možnostech práce s LINQ ve více vláknech. PLINQ je součástí Parallel Extensions, která nabízí další zajímavé možnosti práce s vlákny. Vše podstatné najdete zde.

Místo oběda jsem navštívil krátkou, 45 minutovou přednášku o tom, jak vlastně fungují lambda výrazy a nutno říct, že to byla velice dobře udělaná přednáška, která bohužel proběhla v tzv. theatre a tyto přednášky se nenahrávají, takže na DVD z konference nebude.

Odpoledne jsem jako nejzajímavější absolvoval debatu s názvem „Agilní rozhovor o agilním programování“. Pro ty, co ještě tápou v tom, co se skrývá za pojmem agilní programování, pár myšlenek z debaty:

Velice zjednodušeně řešeno, agilní programování umožňuje tvorbu kódu tak, že na požadavky klienta, které se objeví v průběhu vývoje, jste v stavu říct ANO. Samozřejmě, sem tam mu musíte říct ANO, ALE …

Základem pro tuto schopnost je krátký interval mezi verzemi aplikace, a to na úrovni 1,5 až 3 měsíce. Po dobu vývoje neustále dochází k vyhodnocení priority požadavků co za features kódovat, takže se klidně může stát, že to co mělo na začátku vysokou prioritu, a bylo naplánováno do verze 1.3, se v okamžiku zahájení práce na 1.3 přesune na později a začne se s jinou feature, která se ukáže na základě nových požadavků klienta důležitější.

Zajímavá myšlenka padla u dotazu, jak tímto způsobem řídit vývoj, když je fixní částka peněz domluvená za projekt. Jedno z doporučení bylo řešit to pomocí rozdělení veliké zakázky na několik menších. Takže např. namísto kontraktu za 15 milionů na 3 roky podepsat kontrakt za 5 milionů na první rok s tím, že pokud budou obě strany spokojené, není problém pokračovat ve vývoji další rok s podpisem pokračující smlouvy. I když na první pohled má tento nápad plno mínusů (hlavně když si nejsem jist spokojeností klienta s mou prací po roce), má i výhody a za jednu z hlavních bych viděl překlenutí klasického problému faktu, že klient často na začátku neví, co vlastně přesně chce a stanovení ceny je takové věštění ze skleněné koule.

Aby šlo takové rozdělení realizovat, je nutno projekt rozdělit na malé funkční prvky, které lze udělat a říct hotovo a z těchto prvků pak skládat vyšší funkčnost. No a na těchto malých prvcích pracují „malé“ týmy, které nemají problém koordinovat svůj postup. TFS server samozřejmě podporuje řízení projektů na principech agilního programování, takže tuto metodologii můžete rychle implementovat, pokud TFS používáte. Neznamená to samozřejmě, že bez TFS to nejde.

Ze včerejších přednášek bych rád popsal obsah vystoupení mého oblíbeného prezentátora. Juval Lowy opět nezklamal a pokračuje ve své válce za vítězství programování na základě interfaců.

Úvodní slide začal zostra a v sále to zašumělo. OO označil za jednu z největších chyb při vývoji softwaru. Nejedná se samozřejmě o tvrzení, že by se mělo programovat bez tříd, ale třída jako znovupoužitelný prvek při vývoji SW je to zlo. Pro důkaz svého tvrzení použil myš. Lidská ruka při práci s myší využívá horní kryt myši. Ten je ale reálně použitelný pouze pro daný typ myši, na jinou ho nenasadíte. Co ale je znovupoužitelné je interface, který definuje, že je potřeba umožnit klik na levé nebo pravé tlačítko, dále ovládat kolečko a podobně. Tj. něco co přesně odpovídá popisu interfacu. Definuji výhradně metody, vlastnosti a eventy, ale nic víc. Žádná funkčnost, žádné reálie. Závěrem této části pravil, že pokud náš svět kolem je postaven primárně na interfacech, proč tak nedělat software.

Další, co dostalo zabrat, byla dědičnost. Nevím jak vy, ale já mám taky vždy při overridování metod mrazení v zádech,  co se pokazí, když zavolám base.xxxx() na začátku, nebo na konci nebo když ho nezavolám vůbec, a co se pokazí, až autor rodičovské třídy udělá novou verzi. Tento problém není, pokud jste sami autory celé dědičnostní hierarchie. Ale většinou znovupožíváte hotové třídy a tam je to vždy na pořádné čtení dokumentace. Jenže víme, jak to občas s dokumentací je. Pokud implementujete interface, tak tento problém neřešíte, nakolik není co overridovat a funkčnost si děláte sami.

Základní idea, co teda dělat, bylo řešení interakce mezi třídami na základě interfacu. Nevolat metody instance třídy, ale metody interfacu. Pokud se programátorům nechce, donutíte je explicitním implementováním interfacu.

Několik slidů věnoval generickým interfacům (např. IList<T> ) a jejich používaní jednoznačně doporučoval. Jedno z doporučení bylo nepoužívání omezujících podmínek „where“ při definování generických interfaců nakolik se tím nabourává čistota práce s interfacem.

Co se týče samotného interfacu, doporučil Lowy nepoužívat eventy (děláte tím problémy VB programátorům) a minimalizovat definování property v interfacu. Poměr metody : property by neměl být menší než 2 : 1. Eventy byste měli nahradit deklarací metod s názvem OnXxxxxxx(…) kde Xxxxx je název eventu. Počet prvků v interfacu v ideálním stavu je 3 až 5, maximálně 20. Jeho osobní strop je 12.  Když si dělal průzkum .NETu, napočítal kolem 2.000 interfaců, které mají v průměru 3,5 prvků a poměr metody : property je 3,75 : 1. Eventy definuje míň než 3 % interfaců.

Správný návrh interfacu je samozřejmě to nejtěžší, ale vodítkem co do interfacu dát a co nikoliv, by měla být znovupoužitelnost. Tím že třída může implementovat libovolné množství interfaců, nejste v tomto směru tak omezování jak u jednoduché dědičnosti. Tak hodně štěstí a dobrou ruku při návrhu interfacu. Další zajímavé informace o Lowyho prácích najdete na webu www.idesign.net

 

Po roce jsem se opět vrátil na místo čino, do Orlanda na Floridě, na největší MS akci pro vývojáře - konferenci TechEd.

Poprvé se v USA rozdělil TechEd na týden pro vývojáře a týden pro IT odborníky. V Evropě to tak funguje už od roku 2006, v USA je to novinka. MS očekával 4.000 přihlášek a přihlásilo se 6.000 lidí. Takže je tu opět plno.

Dnešní den zahájil jak je zvykem Key Note. Tentokrát byl hlavní hvězdou sám Bill Gates. Byla to jedna z jeho posledních akcí, kde figuruje ještě v pozici "full-time" šéfa MS. Za měsíc už bude jen na "půl úvazku" a tak připravil na úvod své prezentace film o tom, jak si shání práci Smile Bylo to docela povedené a kdo pohledá, určitě to video v nejbližší době bude někde k mání.

Pak následoval přehled toho, co bude stěžejním pro další směrování vývoje SW. Hlavní změnou má být změna interekce mezi PC a uživatelem (například od fyzické klávesnice a myši ke vstupu z kamery a řízení hlasem, tak jak to dnes známe hlavně z holywoodských filmů). Co se týče prezentační vrstvy, v blízké budoucnosti přinese nové možnosti pro vývojáře IE8, ve vzdálenější se počítá s přechodem od 2D grafiky na 3D. Zásadní vize pak byl do budoucna příklon k deklarativním jazykům (např. WPF či WF pomocí XAML)

Pro vývojáře služeb pracujících s databází byla předvedena novinka rodiny VSTS, a to podpora pro databáze typu DB2 včetně refaktoringu na úrovni DB. Změna názvu sloupce se promítne nejen v schématu, ale i ve všech uložených procedurách. U SQL 2008 padlo zásadní upozornění, že se jedná o rodinu produktů, které jsou nasaditelné jak na PDA, tak na servery s terabyty paměti. Zajímavostí bylo upozornění na drobnosti jako je informace o časovém pásmu u času, či použití FileStream pro ukládní binárních dat (obrázky, hudba). Výhodou je, že nemusíte řešit dilema, kam ukládat obrázky. SQL Server je sám uloží na disk jako soubory, ale bude se o ně sám starat a stanou se i součástí záloh a všeho, co souvisí se správou DB.

Po keynote se rozběhl standarní TechEdí život. Já zahájil přednáškou o novinkách v Enterprise Library 4, která byla uvolněna řed dvěma týdny. Pro ty co používají EL 3.1 je dobrou zprávou, že přechod na 4.0 vyžaduje pouze změnit reference v aplikaci, kód je zpětně kompatibilní. Docela zajímavou částí, která ale bude ještě potřebovat dozrát, byla informace o podpoře WMI2 v EL 4. Jednoduchým zapnutím podpory WMI2 v konfiguraci umožníte napsat aplikace, které se dají přes WMI2 řídit a měnit jejich chování bez nutnosti restartu. Ve spolupráci s PowerShellem ideální kombinace.

Příští rok bude TechEd v Los Angeles v polovině května. Pokud zvažujete zda jít na USA TechEd nebo evropský TechEd, mohu jednoznačně doporučit USA variantu. Pokud někdo bude chtít informace a cenové srovnání nákladů na jednu či druhou akci, napište mi a já Vám poskytnu srovnání nákladů.

 

Outer variables - toto označení mají lokální proměnné a parametry, které použijete v lambda výrazu. Na první pohled to není nic mimořádného, vždyť už od dob anonymních metod si umí C# kompilátor s něčím podobným poradit. Jenže ve spojení s LINQ může nastat situace, kdy to až tak jednoduché není.

Klasické použití může vypadat například takto:

static IEnumerable<int> DoLimitu(IEnumerable<int> pole, int limit)
{
    return pole.Where(i => i < limit);
}

V tomto případě bude vše fungovat jak má. Jenže ve spojení s LINQ a jeho mechanizmem opožděného vykonání dotazu může dojít k nemilému překvapení.

Podívejme se na tento příklad:

static void Main(string[] args)
{

   IEnumerable<int> pole = new int[] { 2, 3, 4, 8, 12, 15, 18, 20, 24, 28, 30, 35, 42, 50 };

   int[] delitele = {2,3};

   foreach(var delitel in delitele)
   {
      pole = pole.Where(n => (n % delitel) == 0);
   }

   foreach (var x in pole)
   {
      Console.WriteLine(x);
   }
}

Výsledkem by měl být tisk čísel, které jsou dělitelné 2 a 3. jenže výstup začne hned trojkou!!!

Důvodem je fakt, že dotaz Where nad polem proběhne až v cyklu foreach a hodnota "outer variable" z lambda výrazu bude 3 (poslední hodnota pole delitele). Řešení tohoto problému je jednoduché, ale je nutné na něj myslet. První cyklus stačí změnit do této podoby:

   foreach(var delitel in delitele)
   {
      int d = delitel;
      pole = pole.Where(n => (n % d) == 0);
   }

V této situaci kompilátor správně pochopí, že d může být vždy jiné a "zapamatuje" pro LINQ všechny hodnoty proměnné d. Pak foreach dopadne korektně. LINQ dotaz se opět vykoná až v druhém cyklu foreach, ale tentokrát se správným a očekávaným výsledkem.

V posledních dvou dnech jsem navštívil několik přednášek, které stojí za krátkou zmínku.

Jako první bych vyzvedl přednášku o metodologii řízení a kontroly SOA projektu. Přednášejícím byl autor knížky o WCF, Juval Lowy. Co všechno zajímavé v poslední době publikoval je k přečtení zde http://www.idesign.net/idesign/DesktopDefault.aspx?tabindex=-1&tabid=9#lowy

V jeho přednášce padlo několik zajímavých doporučení k návrhu SOA aplikace a zároveň metodologie sledování postupu projektu a obrana před vznikajícími problémy. Jako docelá rozumná myšlenky mi přišlo jeho doporučení "jedna service = jeden programátor", pro zamezení vzniku komunikačního šumy a nejasných kompetencí.

Na TechEdu se letos ve veliké míře představuje C# 3.0 a LINQ. Na jedné přednášce, kterou jsem se odhodlal navštívit byla velice pěkná ukázka kódu a možností, které C# 3.0 přinese. Zde je krátká ukázka s vysvětlením (protože jsem to psal v VS2005, tak barvičky nefungují u nových klíčových slov):

List<Klient> seznam = NaplnSeznam();

 

var vyber = from k in seznam

            where k.Mesto == "Brno"

            select k;

 

foreach(var k in vyber)

      Console.WriteLine(k);

 

Uvedený kód prohledá kolekci klientů, najde ty co jsou z Brna a umožní vám s výběrem dál v pohodě pracovat. Ten dotaz samozřejmě podporuje i setřídění, hledání Max, Min, výpočet průměru a pod.

Nový typ var je omezen na lokální proměnné a je věcí kompilátoru, aby z kontextu určil, jaký typ to konkrétně bude.

 

Kdo chce mít vše pod svou kontrolou, může udělat něco takovéhoto:

 

    public delegate bool Vyber<T>(T x);

 

    static class Pokus

    {

        public static IEnumerable<T> Where<T> (this IEnumerable<T> data,Vyber<T> v)

        {

            foreach(T x in data)

                if (v(x))

                    yield return x;

        }

    }

 

    public class Class1()

    {

 

        public static Main()

        {

            List<Klient> seznam = NaplnSeznam();

 

            var vyber = from k in seznam

                        where k.Mesto == "Brno"

                        select k;

 

            foreach(var k in vyber)

                Console.WriteLine(k);

        }

    }

V této variantě se při výběru podle where použije metoda Where ze statické třídy Pokus. V definici metody Where si můžete všimnout nového prvku jazyka, a to použití this v deklaraci parametru metody. Tato navinka má název Extension Methods a přídává danou metodu ke všem třídám typu, u kterého je použito this. V mém případě ke všem třídám implementujícím IEnumerable<T> a tím pádem i seznamu, který je typu List<Klient>.

 

Z dalších přednášek stojí za zmínku přednáška o používání cachování dat v aplikaci. Pěkný nápad je možnost použít ASP .NETí Cache ve WinForm aplikaci. Stačí jen instanciovat HttpRuntime a můžete využít všechny možnosti, které tato cache nabízí. Vliv na výkon aplikace a RAM se neodvažuji teď odhadovat, ale doma si určitě na toto téma pár věcí otestuji. Nová Orca bude nabízet předpřipravené třídy pro vytvoření lokální cache s vazbou na vzdálený SQL Server 2005 pomocí SQL Server Compact Edition, která si udržuje vše v RAM. Bude se to nazývat Sync Services.

 

Ještě malá informace pro ty, co zvažovali účast na PDC konferenci, která měla být v USA v říjnu. Nebude.

 

Místo ní můžete navštívit Dev TechEd v Barceloně. Když jsem si koukal cenu za registraci na konferenci + cenu hotelů, musím jednoznačně konstatovat, že TechEd v USA výjde i s letenkou levněji. Navíc tím že je tato konference nerozdělená pro DEV a IT, můžete si poslechnout i přednášky, které spadají do IT sekce, ale vývojářům mají co říct (například dnes jsem byl na Rusinovitchově přednášce o UAC ve Vistě, která byla v SEC sekci, což spadá právě pod IT). Kdo by chtěl příští rok navštívit TechEd v USA, může si do kalendáře zaznačit datum 9.-13.6.2008 a místo konání bude opět Orlando, FL. Více na stránkách www.msteched.com

 

Druhý dne TechEdu začal příjemně. Byl jsem na přednášce a výhodách a pravidlech použití nástroje NGEN. Přednáška byla označena na úrovni 400 a informace stáli za to. Jakmile si ujasním všechny souvislosti, nabídnu výcuc toho nejdůležitějšího i sem na blog. Zde pár důvodů pro použití ngen na dll knihovny

  • úspora paměti, pokud víc procesů používá stejnou assembly. Při JIT kompilaci si každý proces vytváří svou JIT kopii v paměti, ngen-ovaná assembly je v paměti jen jednou pro všechny procesy
  • rychlost při startu aplikace
  • pokud použijete dobře parametr DLL Base Address, dokážete urychlit natažení a spuštění ngen-ované assembly díky tomu, že odpadne oprava adres při umístění do paměti jinam než určuje BaseAddress

Koho to zaujalo, najde další informace zde: http://msdn2.microsoft.com/en-us/library/6t9t5wcf(VS.80).aspx

Pak jsem navštívil několik přednášek pro architekty o SOA architektuře. Opět se jako hlavní červená nit táhla spolupráce WCF a WF, doplněná o BizTalk a samozřejmě SQL 2005 jako úložiště dat.

Pro ty co používají VS ve verzi Professional, mám dobrou zprávu. VS 2008 bude mít jako svou sučást nástroje pro automatický Unit Testing, to co mají dnes Team Suite verze. Ti co používají něco jiného samozřejmě nikdo nebude nutit, ti co s tím bojují sami případně UT ignorují jako zbytečné zdržování, budou moct využít vše, co VS v tomto směru nabízí.

Tak jsem po roce opět dorazil do USA podívat se na největší MS akci. Letos se koná v Orlandu na Floridě.

Z pohledu vývojáře je vidět jako hlavní téma příští .NET 3.5 a integrace WCF + WF + BizTalk Server. K tomuto se zdá směřují všechny hlavní budoucí nástroje, které nabídne příští VS momentálně nazvané "Orcas". Už se u tohoto VS objevuje sem tam hláška VS2008, takže snad nás tím příští rok MS potěší.

V sekci HandsOnLabů je několik labů jak bývá zvykem nefunkčních (to jest autorům se to drobátko nepovedlo a tak si můžu nechat poslat e-mail, až to bude funkční). Na druhou stranu našel jsem několik labů které mají člověka přivést k použití Vista specifik v .NET aplikaci. Otestoval jsem si WER a TxF, ale laby byly napsané stylem napište toto tamto tuto a teď se koukněte, že to funguje. U WERu to fungovalo jen trošku, TxF byl tak triviální, že by bylo divné kdyby nefungoval. Tak jsem si otestoval, jak se TxF chová, když několik klientů chce dělat se souborem, nad kterým někdo udělal transakci a co se bude dít. CreateFile padal správně do výjimky, pokud jsem chtěl vytvořit soubor, který ještě jakože neexistoval, ale už byl vytvořen v jiné transakci. Delete naproti tomu proběhl korektně v několika transakcích. Netestoval jsem, co by se dělo, kdyby jeden dal Delete a Commit a druhý Delete a Rollback, tak se mám aspoň zíra s čím hrát. Všechny Vista vychytávky jsou zatím přístpné pouze přes P/Invoke nebo COM Wrapper, nic není podporováno direkt z .NETu. O nějaké Vista assembly která by to zastřešila, ani zmínky.

Před měsícem jsem sem psal článeček o výhodách foreach oproti for a jedna z reakci dokazovala, že for je rychlejší než foreach a v uvedeném případě tomu skutečně tak bylo. Nedávno jsem připravoval novou přednášku a při ní jsem si opět hrál s foreach a najednou jsem se nestačil divit. Zde je testovací kód:

int max = 10000;

long suma = 0;

int[] pole = new int[max];

 

for (int i = 0; i < max; i++)

     pole[i] = i;

 

Stopwatch sw = new Stopwatch();

sw.Start();

for (int i = 0; i < pole.Length; i++)

    suma += pole[i];

sw.Stop();

Console.WriteLine(sw.Elapsed.ToString());

 

suma = 0;

sw.Reset();

int delka = pole.Length;

sw.Start();

for (int i = 0; i < delka; i++)

    suma += pole[i];

sw.Stop();

Console.WriteLine(sw.Elapsed.ToString());

 

suma = 0;

sw.Reset();

sw.Start();

foreach (int cislo in pole)

    suma += cislo;

sw.Stop();

Console.WriteLine(sw.Elapsed.ToString());

 

Console.WriteLine("HOTOVO");

 

První for ukazue cyklus for s doporučením jak to psát v C#.

Druhý for je tak, je je doporučován při psaní kódu v C++ a C (hranici do proměnné a pak test podle proměnné)

No a třetí je foreach.

 

A zde výsledky na mém notebooku s 2 GHz Pentiem M a 2 GB RAM. Uvedené časy jsou miliontiny vteřiny.

 

Typ cyklu Debug Release Release přes Ctrl+F5
for ala C# 94,4 76,5 40,5
for ala C++ 91,9 88,0 45,8
foreach 92,1 83,8 13,9

 

Všimněte si zvýrazněného výsledku. V tomto případě iterátor přes foreach je několikanásobně rychlejší. Dále si všimněte, že díky běhu aplikací pomocí hostování v vshost jsou výsledky v prvních dvou sloupcích o ničem.

 

Pokud ale vstoupil do hry plnohodnotně JIT compiler, tak výsledky jsou proti klasickému očekávání. U for podle C# a C++ by dle všech starých pouček měla být rychlejší C++ verze(taky je bez optimalizací v Debug módu) Ale jakmile se jedná o release verzi, tak je hezky vidět, že C# převrátí staré poučky na hlavu. Otázkou je, jak k tomu dojde. Vtip spočívá v tom, že JIT compiler si zkontroluje, že uvnitř cyklu něměníte i a protože ví, že testujete oproti pole.Length, může si být jist, že i určitě nebude mimo rozsah a tak může vypustit kontrolu indexu zda nepřekročí hranice pole. To si při C++ variantě nemůže dovolit, nakolik nemá garanci, co je v proměnné delka.

 

Jak je ale vidět, foreach může být za určitých podmínek výrazně rychlejší než for. Takže až půjde o výkon, nezbývá než testovat, jak si s Vaší konstrukcí kódu poradí JIT compiler a kdy mu to nejvíc "sedne".

 

Opět je vidět, že je nutné věřit "té statistice, kterou si sám připravím..."Smile

Před měsícem jsem tu psal o chování Const a ReadOnly vzhledem ke kompilátoru a o tom, co se stane když změníte hodnotu toho, co je Const.

V souvislosti s tímto článečkem mi dorazil e-mail, kde se tazatel ptal, jak funguje výčtový typ. Téměř jsem odepsal co mi přišlo jako první na mysl, ale pro jistotu jsem si udělal malinkatý test a ověřil, co udělá změna výčtového typu, který je v nějaké referencované assembly.

Zachová se úplně stejně jako kdyby to byl Const. To jest kompilátor při překladu použije hodnotu enumerátoru přímo a nedělá žádný "***" na hodnotu do assembly, kde je výčtový typ deklarován. Takže pokud provedete změnu enumerátoru jinou než jen přidání nových hodnot, tak je nutno zkompilovat vše, co ten enumerátor používá.

Opět situace, kde platí, že dobrý počáteční návrh zabraňuje problémům v budoucnosti. 

 

 

Včera jsem v reakci na komentář k článku o foreach opět po dlouhé době experimentoval s měřením času a narazil na starý problém, že u krátkých operací je měření pomocí DateTime.Now k ničemu. Jeho rozlišovací schopnost končí někde u tisícin vteřiny.

V .NET 1.x nezbývalo než volat API funkce Win32 na přesnější měření. Protože jsem kód pro použití té API funkce někde zašantročil, nezbývalo než začít od začátku a pevná víra mě nesklamala. Našel jsem hezkou třídu v .NET 2.0, jež sluje jménem Stopwatch. Nachází se v System.Diagnostics a práce s ní je intuitivní.

 

// přístup přes foreach

int x;

Stopwatch sw = new Stopwatch();

sw.Start();

 

foreach (int i in list)

{

    x = i;

}

sw.Stop();

Console.WriteLine("foreach: {0}", sw.Elapsed);

 

a teď máte na výběr z vlastností Elapsed, ElapsedMilliseconds a ElapsedTicks, co si vyberete jako měrnou jednotku.
Pak můžete měřit čas dál opětovným zavoláním Start() nebo pomocí Reset() vynulovat počítadlo.

Hezké, jasné a funguje i pro skutečně krátké časy. Já tím změřil 0,000002 vteřiny  (10 průchodů cyklem).

Konstrukce foreach patří k oblíbeným a často používaným nástrojům pro bezpečné procházení kolekcí a polí bez toho, že by si člověk dělal těžkou hlavu z hlídání počtu prvků pole či kolekce.

IEnumerator je interface, který zařizuje vše potřebné. IEnumerator má jednu zajímavou schopnost. Pokud provádíte iteraci kolekce pomocí enumerátoru (typicky pomocí foreach) a po dobu provádění cyklu Vám někdo změní obsah kolekce, dojde k výjimce typu InvalidOperationException. Tady je ovšem nutno si dát pozor na to, co všechno změnu zahrnuje.

Na změnu obsahu objektu, který je zahrnutý v kolekci, IEnumerator nezareaguje nijak. Pokud ale změníte kolekci jako takovou (přidáním prvku, odebráním, nebo změnou reference), dojde v nejbližší iteraci k výjimce.

Tento mechanizmus kontroly je zajištěn velice jednoduše. Každá kolekce si udržuje interní číslo verze, které se mění s každou změnou kolekce (volání Add, Remove, Insert, ...). IEnumerator při svém vzniku si zapamatuje číslo verze kolekce a při každé iteraci kontroluje, zda je toto interní číslo stále stejné. Pokud se změní, nastane výjimka. 

Je zajímavé sledovat vývoj dokumentace .NETu k IEnumerator. První byl součástí namespacu System.Collection a v části Remarks najdete tvrzení o kterém lze polemizovat, a to že při modifikaci kolekce v průběhu iterace dojde k InvalidOperationException. V .NETu 2.0 se zjevil nový IEnumerator v namespacu System.Collection.Generic a u něj je už jen napsáno, že při změně kolekce se enumerátor dostane do nedefinovaného stavu a že je nutno použít nějaký synchronizační nástroj, pokud chcete mít jistotu, že Vám nikdo kolekci pod rukama nezmění.

Tady bych rád zdůraznil, že lock(mojeKolekce) neznamená automaticky zajištění, že v kolekci nedojde k žádné změně!

Další věc, kterou je foreach specifický je fakt, že proměnná, pomocí které procházíte foreach je readonly. Takže nelze udělat:

...
int[] pole = { 1,2,3,4,5,6,7,8,9 };
foreach(int x in pole)
      x = x * x;
....

Ale pokud bude ve foreach procházena kolekce referenčních typů, toto readonly bude znamenat, že přiřazenou referenci nejde změnit, ne že nejde změnit obsah objektu, na který daná reference ukazuje.

Kterou z konstrukcí budete preferovat, záleží na konkrétní situaci. foreach je výhodnější pro .NET kompilátor. Ten pak generuje kód, kde odpadne část nutných kontrol, které vyžaduje přístup přes index. Na druhou stranu, možnost získání informace, že se Vám kolekce v průběhu iterace změnila, stojí trochu výkonu. No a je na Vás se rozhodnout, co je pro Vás důležitější. (Tímto děkuji autorům komentářů. Na základě jejich připomínek došlo k úpravě tohoto odstavce)

Více článků Další stránka »
 
Vyvojar.cz na prodej!