CSLA.NET aneb jak si framework nepředstavuji (část II.)
V minulé části jsem si neodpustil poněkud drzé prohlášení o frameworku CSLA. Nyní mě čeká část obhajoby svých tvrzení.
V předchozím článku jsem uvedl kostru běžného Business objektu. V této části uvedu jednotlivé detaily částí třídy, které jsou v kostře pouze slovně popsané v podobě komentářů a to pro případ Business objektu reprezentujícího model zaměstnance v informačním systému.
Pod komentářem // atributy se skrývá obvykle takovýto kód.
private string _name;
public string Name
{
get
{
this.CanReadProperty(“Name“, true);
return _name;
}
set
{
this.CanWriteProperty(“Name“, true);
_name = value;
PropertyHasChanged(“Name“);
}
}
Vidíme atribut name zapouzdřený standardním způsobem v C#, tedy pomocí property. Metody CanReadProperty a CanWriteProperty jsou metody z bázových tříd, které mají ověřit přístupová práva. Všimněme si hned několika nevýhod:
- název atributu, pro který jsou testována přístupová práva je uveden v metodě jako řetězec ( „Name“ ). Je velmi snadné udělat překlep – překladač nás v tomto případě vůbec neohlídá. Existuje sice ještě přetížená metoda this.CanReadProperty(true) bez tohoto parametru, kdy framework získává ze stacku volání metody název atributu, avšak tento přístup je v nové verzi frameworku zrušen (pokud je mi známo) a ve starší má „drobný“ problém – překladač může getter nebo setter přeložit jako inline metodu a pak framework při zjišťování názvu property neuspěje (dá se tomu zabránit uvedením následujícího atributu před metodu get a set, ale není to zrovna elegantní – [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] ).
- ponechávat zjištění přístupových práv přímo na testovaném objektu se mi nezdá rozumné – neodpovídá to ani reálnému světu – přeci zákazník se také nechodí ptát za ředitelem, jestli může vstoupit, od toho má ředitel sekretářku a vrátného. Z hlediska AOP se zde také jedná přímo o modelový příklad aspektu, uvědomme si, že volání této metody se vyskytne u každého atributu každého Business objektu.
Nutnost volání metody PropertyHasChanged má svůj původ v data bindingu a způsobu jeho implementace v CSLA.NET frameworku. Pro obousměrný data binding je totiž nutné, aby datový objekt informoval ovládací prvek o změně dat. Co se nutnosti voláná této metodý týká, jedná se opět o aspekt.
V případě atributů nám tedy framework CSLA.NET nepomohl. Spíše nás donutil napsat 3 řádky kódu navíc pro každý atribut (sečteno pro metodu get a set). Uvidíme, jak si CSLA.NET povede v další části – vytvoření instance Business objektu a získání instance Business objektu naplněného daty z databáze.
Doporučený způsob vytváření instancí (ať nových objektů nebo naplněných daty z databáze) je statickou metodou Business objektu. Vypadá obvykle takto jednoduše:
public static Zamestnanec NewZamestnanec()
{
if (!CanAddObject()) {
throw new System.Security.SecurityException(“Uživatel nemůže přidávat zaměstnance.“);
}
return DataPortal.Create<Zamestnanec>();
}
public static Zamestnanec GetZamestnanec(Guid id)
{
if (!CanGetObject()) {
throw new System.Security.SecurityException(“Uživatel nemůže načíst zaměstnance.“);
}
return DataPortal.Fetch<Zamestnanec>(new Criteria(id));
}
Metoda CanGetObject() vypadá v šablonách tříd frameworku, které následně dopisujeme, asi takto:
public static bool CanGetObject()
{
// TODO: customize to check user role
//return ApplicationContext.User.IsInRole(“");
return true;
}
a je tedy nutno upravit si jí podle svého. Tedy pro každý Business objekt a každou jeho metodu pro testování přístupových práv nám nezbývá nic jiného, než si ji napsat vlastníma rukama.
Nastal čas zmínit se o jedné z nejčasteji využívaných tříd frameworku – třídě DataPortal. Ta se stará o vyvolávání některých metod Business objektu na straně klienta nebo serveru (podle konfigurace aplikace). Jedná se o metody Create, Update, Fetch, Delete a Execute. Poslední z nich slouží pro vyvolání tzv. commandů – tedy kódu, který je implementován potomkem třídy CommandBase, a který umožňuje vykonání vlastně libovolné operace. Typickým příkladem commandu je zjištění, zda zaměstnanec se zadaným osobním číslem již existuje v databázi, jako součást validace zaměstnance. Tento kód je nutno v případě konfigurace podporující vzdálené volání vykonat na serveru, proto bude implementován jako command. Ostatní metody jsou předurčeny konkrétním funkcím – odpovídají jejich názvu.
DataPortal nás odstiňuje od síťové komunikace a od práce s remotingem, web services a podobně. Je vstupním bodem pro všechny části, které se mají vykonávat na serveru (pokud konfigurace tento způsob nařizuje).
Pokud se vrátíme k výše uvedenému kódu, moje námitka bude směřovat hlavně k testování přístupových práv při vytváření instancí. Podle autora je zřejmě vhodnou praktikou testovat přístupová práva explicitně voláním metody uvnitř statické metody pro získání instance. Opět se jedná o aspekt. V tomto případě však navíc opomenutí tohoto testu přístupových práv může být velmi závažnou chybou, pokud se objeví na citlivých datech až v případě zneužití nějakým narušitelem systému.
V této části nám framework pomohl vlastně pouze odstíněním od síťové komunikace v případě distribuované aplikace a tím, že stanovil jediný konfigurovatelný vstupní bod pro vykonávání metod na straně serveru. Vykonávání jiných metod, než výše uvedených základních metod pro CRUD je však poněkud zdlouhavé – je nutno zapouzdřit kód do tvaru commandu.
Pro lepší představu uvedu ještě průběh volání statické metody pro získání instance Business objektu z databáze voláním výše uvedené statické metody GetZamestnanec():
- zavoláme metodu třídy Zamestnanec – GetZamestnanec(Guid id), které předáme jako parametr identifikátor zaměstnance.
- je provedeno volání statické metody třídy DataPortal, která provede přenesení vytvořené instance třídy Criteria na server a provede volání metody DataPortal_Fetch(Criteria criteria) na nově vytvořené instanci třídy Zamestnanec na serveru.
- instance třídy Zamestnanec s hodnotami nastavenými daty z databáze je vrácena na klienta.
Žádnou magii tu nehledejte.
Při pohledu na hodinky myslím, že další popis nechám na nějaké příští pokračování.

