| Árvai Zoltán's profileÁrvai Zoltán BlogjaBlogLists | Help |
Árvai Zoltán Blogja |
||||
|
January 04 Lusta és Mohó kiértékelés ADO.NET Data Services-benEgy fórum post késztetett arra, hogy írjak erről a bizony egyáltalán nem elhanyagolható témáról. Valójában egy nem túl dokumentált technológiáról van szó, főleg ha a hozzátartozó Linq providerről van szó. Szóval a kérdés, hogy hogyan töltsük be egy entitáshoz tartozó gyermek entitáshalmazt (nyilván valami reláción keresztül), sőt mi van akkor ha a gyermek entitáshalmazhoz tartozó egyik gyermek entitáshalmazt is be szeretnénk tölteni, és ezek kombinációját mindenki el tudja képzelni. Továbbá az is jó kérdés, hogy egyáltalán mikor töltsük be ezeket az adatokat? Rögtön egyetlen lekérdezéssel mindent húzzunk le (mohó kiértékelés), vagy esetleg akkor töltsük be a kapcsolódó entitásokat amikor ténlyeg szükség van rájuk (lusta kiértékelés)? A bejegyzéshez tartozó demó adatmodell a Northwind adatbázis alapján készült és azon belül annak egy részhalmazára, egészen pontosan a Customers, Orders, OrderDetails, Shippers 4-essel foglalkozunk most. A példa alkalmazás egy egyszerű WPF kliens lesz, amelynek segítségével egy DataGrid-ben (WPF Toolkit része) jelenítjük meg az adatokat. A Cél a következő: a Customereket jelenítsük meg alapból, kiválasztva egy Customer-t lássuk a hozzátartozó Order-eket illetve az egyes orderekhez kapcsolódó Order_Details-eket. (Azaz a vásárlók megrendeléseit és azok részleteit) Mivel ADO.NET Data Services-ben nincs join, így csak a modell alapján kialakított kapcsolatokon tudunk navigálni. ALFKI ID-val rendelkező felhasználó A hozzá tartozó egyik megrendelés. Kézen fekvő lenne, de ilyet sajnos nem lehet... (A vásárló adott megrendeléséhez tartozó részletek) Innentől kezdve ez sem megy... A feladat egy pontig az előző gondolkodás nyomán megodható: A várásló egy adott megrendlése Azután a megrendléshez tartozó részletek Azonban az egész megoldható egyetlen lépésben is, ún. eager loading (vagy mohó kiértékelés) segítéségével A fenti példában az adott vásárlónk Megrendeléseit még le tudjuk kérni, utánna kérjük explicit módon az expand operátor segítségével az egyes megrendelésekhez tartozó össze részletet is. Valami ehhez hasonló eredmény kapunk, ahol jól látható, hogy az 10643-as megrendelésen belül az ahhoz tartozó tulajdonságokon kívül ott szerepel még a teljesn Order_Details entitás (és még kettő bezárva a lap alján). Szóval ilyen formában az egész eredmény halmaz lekérdezhetővé vált. Természetesen a szintaxis nagyon sokat megenged, játszatunk kedvünkre: Az összes megrendelőhöz tartozó összes megrendelés Az összes megrendlőhöz tartozó összes megrendenlés és azok részletei Nézzük ezek ismeretében hogyan írjuk meg kliensünket? Két változatot is követünk, az egyik a mohó kiértékelés, a másik a lusta kiértékelés 1. Eager Loading (mohó kiértékelés)Én erőssen Linq To ADO.NET Data Services párti vagyok, szóval nézzük, hogy az előbbi url-ek hogyan állíthatók elő Linq segítségével. Szerencsére elég egyszerűen :) Az fenti query-t futtatva és debuggolva azt kapjuk, hogy az egyes Customers-ekben nincs egyetlen megrendelés sem. (szép is lenne, ha csak úgy magától betöltené...) A megoldás a query-nk átalakítása és az Expand() Extension method használata. A második query-t futtatva meggyőződhetünk róla, hogy az adatok valóban letöltésre kerültek Külön szeretném felhívni a figyelmet a kékkel bekeretezett részre, ahol látjuk, hogy mifél query megy a service-ünk felé. (remélem ismerős :P) Már csak valami egyszerű megjelenítő felület kell hozzá, és mivel WPF-ben vagyunk gyorsan 3 datagrid alkalmazással vizualizáljuk az eredményt. Egy datagrid a vásárlóknak, annak rowdetailstempalte-jébe egy másik datagrid a megrendeléseknek, majd annak a rowdetails-ébe egy harmadik datagrid a megrendelések részleteinek. Nagyjából a következő eredményt sikerült elérnünk, ha futtatjuk, majd kiálasztunk egy megrendelőt, majd a hozzá tartozó egyik megrendelést. A mohó kiértékelés hátránya, hogy nagy mennyiségű adatot tölt lesz egyszerre, ugyanakkor a letöltést követően az adatok és a részletek azonnal elérhetők lesznek. 2. Lazy Loading (lusta kiértékelés)Node mi van akkor, ha a nagy mennyiségű adatok letöltése (rengeteg felesleges információ, amire nincs is szükségem) számomra nem elfogadható. Mi van akkor, ha az előző gridbe csak azokat az adatokat szeretném letölteni, amiket a felhasználó kinyit. Igaz ekkor azzal kell számolnom, hogy minden kinyitásnál várni kell az adatok letöltésére, de talán ez elfogadhatóbb, mint több 10 ezer rekordot letölteni.... Ez az ún Lazy Loading vagy lusta kiértékelés segítségével lehet. Hallelúja Linq To ADO DS, ezt is tudod! Miután szereztük referenciát egy objektum példányra (amihez tartozó egyéb adatrekordokat szeretnénk megjeleníteni, pl theCustomer.Orders) a DataServiceContext-ünknek azonnal jelezni kell, hogy mit szeretnénk betölteni. (ez egy újabb HTTP GET lesz!) A fenti példában minden egyes customer-hez külön(!!!) betöltésre kerül a customerhez tartozó megrendelés! Ha a fenti példát szeretnénk átírni lazy loading-ra, akkor a következőt tehetjük: Fel kell iratkozni a külső datagrideken a LoadingRowDetails eseményre. Itt elkövetjük a késő betöltést, majd frissítjük a datagridet (mert az adatmodellen sajnos változás értesítésnek nyoma sincs :( ) Silverlight alatt sem sokkal bonyolultabb a dolog, csak az aszinkron megvalósításra kell figyelni. Akit érdekel a pontos implementáció, a forráskódot letöltheti az alábbi linkről: December 02 ADO.NET Data Services és a Silverlight 2.0Legutóbb ott hagytuk abba, hogy megismerkedtünk az ADO.NET Data Services-zel, áttekintettük, hogy miként lehet lekérdezéseket megfogalmazni az URL segítségével, valamint hogyan lehet a szolgáltatást jobban testreszabni interceptorok, illetve service operation-ök segítségével. Most azt tekintjük át, hogy miként tudjuk Silverlight 2.0-ban használni az ADO.NET DS-t, hogyan lehet CRUD műveleteket kezdeményezni. Referencia felvétele a szolgáltatásra Az előző projektben elkészített Data Service tökéletes lesz, mindössze az interceptorokat távolítottam el belőle. (Ha vmilyen okból kapnánk egy error message-et az OK-ra klikkelést követően, amire sajnos van esély, akkor a felvett referencián egy jobb klikket kell nyomni és az Update Reference-t kell választani. Megjavul :) ) Na nézzük mi minden történt. A Solution Explorer-en klikkeljünk a show all files-ra, és nyissuk ki a DataServiceReference-t! Nocsak! Service.edmx?? Entity Framework kliens oldalon? Ha belenézük, bizony úgy néz ki, mintha a Conceptual Models lenne benne! A reference.cs-t kinyitva bizony meglepődök: Products, Categories entitás osztályok, valamint NorthwindEntities, ami a DataServiceContext osztályból származik. Gondolom ezen a ponton már ti is rájöttetek miről van szó... IGEN! Az adatmodellem (egy webszerveren van) kipublikáltam egy szolgáltatáson keresztül, amelyhez tartozó kliensoldali proxy gyakorlatilag kliensoldali változáskövetést biztosít számomra!!! Bizony... szerveren futó adatmodellhez, kliensoldali változáskövetés, miközben a kettő egy webszolgáltatással kommunikál, transzparens módon. Vegyük hát használatba! Lekérdezések építése Linq To ADO.NET Data Services segítségével Nem vicc! Nem csak a URL alapú lekérdezéseket konkatenálhatjuk, hanem használhatjuk a Linq To Ado.Net DS provider-t, ami ezt a munkát elvégzi helyettünk! Gyorsan felvettem egy ListBoxot a felhasználói felületemre, hogy az eredményt megjeleníthessem! Most már csak a lekérdezést kell megírni, irány a Page.xaml.cs Page() konstruktora. Először is a Proxy osztályt kell megpéldányosítani, a proxy konstruktora egy URI objektumot vár, amely a szolgáltatás címét tartalmazza.
Kérdezzük le az összes terméket, és jelenítsük meg a listboxunkban. Íme az első próbálkozás: Az arcunkba egy óriási Exception-t kapunk! Jobban megvizsgálva a kivételt, annyit sikerül kimazsolázni, hogy bizony ez a művelet nem támogatott! Na de milyen művelet? Bizony Silverlightból nem tudjuk a data service-ünket szinkron módon meghívni, csak is aszinkron műveletek támogatottak! A query-nek viszont nincs semilyen aszinkron művelete, ami segíthetne a lekérdezésben, ezért egy külső (ős) osztályt kell segítségül hívni! Ez pedig a DataServiceQuery<T>: Szóval a mezei query objektumunkat átcastolva a DataServiceQuery<Products>-ra sikerül szert tennünk egy BeginExecute metódusra. Az aszinkron műveleteknél megszokott módon, szükség van egy Callback függvényre, ami meghívásra kerül, ha az aszinkron művelet véget ér, második paraméterként pedig átadjuk az objektumot, amin a változás beáll. Íme a callback függvényünk! Egy paramétert fogad, ez pedig az IAsyncResult típusú változónk. A korábban átpasszolt dsQuery-hez itt a result.AsyncState objektummal férünk hozzá (ez object), de még át kell castolni a megfelelő típusra! Picit testreszabva a megjelenést az alábbi kóddal (DataTemplate)... ...az alábbi eredményt kapjuk: Szóval lekérdezéseket írni nem bonyolult, futtatni sem vészes, csak szem előtt kell tartanunk, hogy bizony aszinkron kommunikáció mehet csak, ha Silverlight 2.0 a kliensünk. A lekérdezés probléma nélkül lefut, az eredmény az alábbi ábrán látható. Mit csinál a háttérban a Linq To ADO.NET Data Services provider? Természetesen összerakja az URL alapú lekérdezést. Íme az előző lekérdezés debug módban. Ez nagyon kellemes, ugyanakkor figyelembe kell vennünk ezt a tényt, minden egyes query írásánál. Számos korábban megszokott linq művelet nem fog működni, ugyanis csak azok a műveletek támogatottak, amit át lehet írni URL alapú lekérdezésbe. Új elem felvétele Elhelyeztem egy gombot a felhasználó felületen, amelynek eseménykezelőjében veszek fel egy új terméket. (A NorthwindEntities-t közben kiraktam a Page osztályba privát változóként) Ugye milyen kellemes az a Products.CreateProducts() metódus? (végre nem az adatbázisból kell kitúrni, hogy mi a null és mi a nem null, ha demózok :) ) A dc-nek van egy AddToProducts metódusa, ami felveszi az új product-unkat a Products gyűjteménybe! Az SavingCompleted metódusban olvashatjuk ki a szolgáltatás által küldött választ, hogy miként is sikerült ezt a beszúrás dolog. Jól megvizsgálva a response belsejében feltűnik, hogy az imént beszúrt elem a "107-es" ID-val sikeres volt (hiába adtam én meg 1-et id-nak, autoinkremens, ennek örülök:) ). Viszont a listában (ha a szűréseket kizárjuk) nem jelenik meg az új termék! Irány a Reference.cs, hogy megnézzük miért. Látjuk, hogy a Products, Categories gyújtemények DataServiceQuery<T> típusúak. Ha megnézzük ennek a típusnak a definícióját látjuk hogy nem valósítja meg az INotifyCollectionChanged interface-t. Marad a kézi updatelés, újralekérdezés, valamilyen mechanikus frissítés, esetleg vmilyen wrapper class az egész köré. :( Ha netán valami miatt nem jönne össze a beszúrás (pl primarykey egyediségének megsértése, ahol nem autoinkremens) akkor kapunk egy szép DataServiceRequestException-t, ami az EndSaveChanges hívásnál érkezik meg (aszinkron esetben), és hát nekem nem sikerült kibogarászni belőle a hiba jelleggét. Na szóval érezni, hogy a hibakezelésre (konkurrencia problémák stb..) egy külön fejezetet szentelhetünk. (meg is tesszük majd :) ) Meglévő elem módosítása. Változáskövetés tudjuk, hogy van, property-t módosítunk. Az UpdateObject metódus, modified állapotba billenti az entitás példányunk, majd kiadjuk a BeginSaveChanges hívást (lásd korábban), ami a módosult entitásokat felszinkronizálja. A korábban készítet SavingCompleted callback függvény jó is lesz. Ez kézenfekvő... Azonban a listában nem változik az adott property neve! Ha ismét átmegyünk a reference.cs-be láthatjuk, hogy a Products osztály és a Categories osztály nem valósítja meg az INotifyPropertyChanged interface-t sem. Szóval nem csak a listában történő változásokat nem fogjuk észrevenni, de az objektumok propertyjeiben beállt változásokat sem :( Erre workaround lehet (az újra lekérdezésen kívül), ha megnézzük, hogy az egyes propertyk setterében a partial OnXXXChanged metódus meghívásra kerül. Tehát készítünk egy saját Partial Product osztályt (kiegészítjük a meglevőt), implementáltatjuk vele az INotifyPropertyChanged interface-t, majd az eseményt ki is váltjuk. Ezzel az apró kiegészítéssel a ProductName property változásakor jelentést kapunk az eseményről, így a felhasználó felület is kiértesül, és frissíti az elemet! Meglévő elem törlése. Kiválasztjuk a törlendő elemet (linq to objects), majd a DeleteObject() metódus segítségével töröljük. A változások mentése a megszokott mederben folyik! Sajnos a felhasználói felület nem értesül a változásról, ezt ismét nekünk kell elintézni. Remélem ízelítőnek ennyi elegendő volt. :) A folytatás(ok)ban batch updateről, konkurrencia kezelésről, hitelesítésről, relációk bejárásáról, kapcsolódó entitások betöltéséről lesz szó. December 01 ASP.NET Dynamic Data Intro (2.)Legutóljára ott hagytuk abba, hogy valahogy jó lenne az adatok validálását is jobban kézben tartani, valamint szeretnénk egyes táblák megjelenítését egyénire szabni. Ez a bejegyzés most az előbbi, validációs témába nyújt egy rövid betekintést. ADO.NET Dynamic Data - Bemeneti adatok validálásaAz előző alkalmazás mentén tovább a haladva, ha futtatjuk a projektet, és átnavigálunk a Customers táblához, egy sort szerkesztve láthatjuk, hogy megtehető, hogy a ContactName mezőt üresen hagyjuk és úgy updatelünk.
Validálás metaadatokkal input szinten Készítsünk egy metaadat osztályt a Customers entitáshoz is, ahol konfigurálhatjuk a validációs mechanizmust. 1. RequiredFieldValidator A ContextName mező kitöltése a Required attribútumnak köszönhetően kötelező, valamint a default "The ContactName field is requied" helyett "A ContactName mezőt ki kell töltenie" üzenetet kapjuk az ErrorMessage beállításának köszönhetően. 2. RangeValidator A Products osztályban a UnitsInStock property értéke csak 0 és 1000 közé eshet! 3. StringLenghtValidator Jól jöhet, ha definiálhatjuk, hogy maximum hány karakter hosszú stringet rendelhetünk egy értékhez. Különösen kellemes, ha a Dynamic Data ennek megfelelően a TextBoxunk MaxLength property-jét is rögtön beállítja, azaz nem tudok X karakternél többet beírni a szövegdobozba. Ezt a StringLengthAttribute segítségével érhetjük el. Jelen esetben az ErrorMessage még felesleges is hiszen, eleve nem tudunk 15 karakternél többet bírni a Country-hoz tartozó TextBox-ba! Erre a bizonyítékot a kirenderelt HTML-ben találhatjuk meg: 4. RegularExpressionValidator A legrugalmasabb beépített validációs mechanizmus talán a reguláris kifejezés alapján történő validáció. Az alábbi példában a Shippers táblában található Phone mezőhöz füzök egy fix telefonszám formátumot (xxx) xxx-xxxx formában. Segítségemre a RegularExpressionAttribute siet. A kimeneten pedig jól látszik az eredmény: Validálás adatmodell szintjén Időnként ennél is rugalmasabb validációra van szükség. A saját validációs logikát elhelyzetjük az adatmodellben is, akár több szinten. 1. Validálás Property szinten Ha mindössze egyetlen property-t szeretnénk validálni megtehetjük a property-hez tartozó, helyesebben fogalmazva, a property setterében meghívásra kerülő parciális metódusokban. Vessünk egy pillantást a Customers osztály ContactTitle mezőjére: Látjuk a setter-ben, hogy a _ContactTitle privát változó beállítása előtt meghívásra kerül az OnContactTitleChanging(string) metódus. Hol van ennek a metódusnak a definíciója? Néhány sorral lentebb látható: partial void OnContactTitleChanging(string value). Azaz a prototípus definiált, de implementálva nincs. Mivel partialként van megjelölve a meghívása nem okoz problémát, lehetőséget kapunk, hogy később implementáljuk ezt a metódust. Ezt tesszük meg most! Ha a custom logikánk (amit egy RequiredAttribute-tal is kiválthattam volna, mindenki saját fantáziájára bízom, hogy milyen custom logikát alkalmazna itt) szerint érvénytelen az input, akkor egy ValidatonException-t eldobunk. Na jó, de ki kapja el? A válasz az adott pagetemplate-ben található! Jól látható, hogy egy DynamicValidator helyezkedik el az Edit.aspx-ünkben, amely a DetailsView1-et igyekszik validálni. Na ő az, aki elkapja ezeket a ValidationException-öket és megfelelően kezeli. (Ismét valami, ami kész van, különösebb kódolás nélkül) 2. Validálás Entitás szinten (Linq To Sql) Ha netán nem egyetlen property, hanem egy egész entitás szintjén szeretnénk validálni, erre is van lehetőség, mégpedig az 3. Validálás Contextus szinten (Entity Framework) A contextus osztályunk (NorthwindEntities) rendelkezik egy OnContextCreated parciális metódussal, melyben feliratkozhatunk a SavingChanges eseményre, ahol az összes függőben levő változtatáson végigmazsolázhatunk, és saját validációs logika alapján akár ValidationException-t is dobhatunk. Ahogy korábban is, a kivételt a DynamicValidator kapja el és jeleníti meg. Remélem ízelítőnek elég lesz ennyi a validáció testreszabásáról az ASP.NET Dynamic Data-ban. A következő bejegyzésben a táblák megjelenítésének egyéni testreszabásáról lesz szó. November 27 ADO.NET Data Services IntroductionAz ADO.NET DS célja és szerepeFejlesztés során időnk jelentős részét az teszi ki, hogy adathozzáférési/adatszolgáltatási rétegeket gyártunk. Ha eljutottunk odáig, hogy sikerült kinyernünk az adatbázisból a szükséges adatokat, a következő feladatunk, hogy azt megfelelő formában átadjuk a kliens alkalmazásunknak, mondjuk HTTP-n keresztül. A másik irány se sokkal kellemesebb, a létrehozott entitás példányokat kell egy webszolgáltatás segítségével beszúrnunk az adatbázisunkba. Eddig két irányban gondolkodhattunk. Mezei webszolgáltatásokban (asmx), vagy WCF-es szolgáltatásokban. Képzeljük el a következő helyzetet. Kell csinálnunk egy GetProducts metódust, ami visszaadja az összes termékünket. Aztán rájövünk, hogy többféle szűrést is alklamazunk kell, így elkészülnek a GetProductByID, a GetProductsByCategory, GetProductByName stb. metódusok. Nyilvánvalóvá vált, hogy szükség van egy olyan rugalmas adatcentrikus szolgáltatásra, amely az összes szükséges, ilyen jellegű CRUD (create / read / update / delete) műveletet automatikusan előállítja. Ezt a mára már robot, vagy szolgamunkának nevezhető terhet, igyekszik az ADO.NET Data Services levenni a vállunkról.
Mit nyújt az ADO.NET DS?Az ADO.NET Data Services az adatbázisunk fölé generál egy adatcentrikus szolgáltatás réteget, amely támogatást nyújt a leggyakoribb feladatokra, mint a CRUD műveletek, a lapozás, szűrés, rendezés, vagy az egyszerű, adatok közti navigáció. Az adatok kipublikálása ATOM és JSON segítségével( ez utóbbi különösen jó hír az AJAX fejlesztők számára), a hozzáférés pedig URL alapon történik, ahol az URL a szolgáltatás címének, illetve az entitások és a műveletek nevének összefűzéséből jön létre. Így bármilyen alkalmazás számára könnyen hozzáférhetővé válik. A szolgáltatás az adatbázisból az adatok kiolvasására, olyan ORM (object relational mapping) eszközöket hívhat segítségül, mint az ADO.NET Entity Framework, vagy a Linq To Sql. Meg kell jegyeznünk (bár ez már sejteti), hogy bármilyen CLR objektum tekinthető adatforrásnak és kipublikálható, ha az implementálja az IQueryable<T> interface-t.
ADO.NET DS Szolgáltatás készítseAz ADO.NET DS-nek szüksége van az adatmodellre, ami fölé generálhat szolgáltatás réteget. Ezért az első lépés egy adatmodell elkészítése. Itt most a Northwind adatbázishoz készítünk egy adatmodellt. (lásd: Entitás modell elkészítése) Vegyünk fel egy új elemet a projekthez, ami egy ADO.DET Data Service legyen. Megnyitva a NorthwindDataSerivce.cs-t, a következő látvány tárul elénk: Azonban még nem vagyunk kész! Ugyanis ha most elindítanánk az alkalmazásunkat egy üres service document bámulna ránk. Nem mutatna egyetlen entitást sem. Az entitásokhoz a hozzáférést egyenként engedélyezni kell. Ezt a szolgáltatás InitializeService metódusában tehetjük meg a config.SetEntityAccessRule hívással: Itt most a két táblára teljes jogosultsági kört engedélyeztünk. Természetesen ez is testreszabható az EntitySetRights enumerációval. Az ADO.NET DS használatba vételeFentebb már említsére került, hogy az ADO.NET DS szolgáltatásunkhoz való hozzáférésre URL alapú megközelítést alkalmazunk. Ez egy egyszerű címzési séma. A formátum a következő:
Az URL-t ennek megfelelően 3 részre bonthatjuk. 1. A szolgáltatás elérési útvonala pl. http://localhost:6627/WebSite15/NorthwindDataService.svc/ Ezen a címen, hogy XML fájlt, az ún. Service Document-et kapnánk, amely egy leírás arról, hogy az adott szolgáltatás milyen adatokat publikál ki. (lásd legutóbbi ábra) 2. Az entitás neve pl. http://localhost:6627/WebSite15/NorthwindDataService.svc/Products A kapott eredmény egy AtomPub formátumú dokumentum, amelyben az összes termék adatai megtalálhatók. Az entitás neve mögé szűrható zárójelben az elsődleges kulcs, ami alapján egy adott terméket kiválaszthatunk. pl. http://localhost:6627/WebSite15/NorthwindDataService.svc/Products(11) 3. Navigációs property pl. http://localhost:6627/WebSite15/NorthwindDataService.svc/Products(11)/Categories Ha a két entitás között van reláció, akkor azon a kapcsolaton könnyen mozoghatunk. A kapott eredmény a 11-es ProductID-val rendelkező termék kategóriájának részletei. Megjegyzés: Egyéb kulcsszavak is használhatók például, rendezésre, szűrésre, metaadatok kiolvasásra stb... Az ilyen kulcsszavakat az URL után egy ”?”-et követően sorolhatjuk fel $kulcsszó szintaktikával. Kulcszavak egymásután füzése a ”&” karakterrel lehetséges. Például: http://localhost:6627/WebSite15/NorthwindDataService.svc/Products?$orderby=ProductName Azaz rendezzük a termékeket nevük szerint ábécé sorrendben. http://localhost:6627/WebSite15/NorthwindDataService.svc/Products?$orderby=ProductName&$filter=UnitPrice gt 100 Csak az 100-nál drágább termékeket jelenítjük meg abc sorrendben. Az ADO.NET Data Services lényegében minden .NET-es klienssel képes együtt működni, legyen az WPF, WinForms, Silverlight, AJAX, vagy egy sima Console-os alkalmazás. Egy lépéssel ismét közelebb jutottunk a gyors alkalmazás fejlesztés világához. NorthwindService.svc/Customers(’ALFKI’) ADO.NET Data Serivces - Saját műveletek (ServiceOperation)Ha olyan műveletet szeretnénk végrehajtani, amit URL alapú lekérdezéssel nem tudunk, akkor készíthetünk saját műveleteket, ún. Service operation-öket. A visszatérési érték IQueryable<T> annak érdekében, hogy a lekérdezés tovább alakítható legyen. Akárcsak az entitáshalmazokat, a ServiceOperation-t is engedélyezni kell: Ezt követően a metódus hívható az alábbi módon: ADO.NET Data Services - Lekérdezések felügyelete(QueryInterceptor)Előfordulhat, hogy szeretnénk kontrollálni az entitásokhoz a hozzáférést, szűrni a visszaadott eredményhalmazt stb. Ezt QueryInterceptor-okkal tehetjük meg. A QueryInterceptor attribútumban jeleztük, hogy a Products entitáshalmazhoz tartozik az interceptor. Ez a metódus egy lambdakifejezést ad vissza, ami részt vesz a végső Query felépítésében. Azaz, ha egy feltételt ide (szűrést) ide elhelyezünk, az a végső eredményhalmazt befolyásolja, ezért ez a bonyolult visszatérési érték. (Productokon értelmezünk egy logikai (bool) kifejezést, amiből kifejezés fát építünk) Ha futtatjuk a szolgáltatás és a Products entitáshalmazt vizsgáljuk, láthatjuk, hogy csak az italok jelennek meg. ADO.NET Data Services - Módosítások felügyelete(ChangeInterceptor)Nem csak a legkérdezés, de az adatmódosítás is vezérelhető interceptorok segítségével. A ChangeInterceptor attribútum paramétereként megadjuk, hogy melyik entitáshalmaz módosítását szeretnék figyelemmel követni. A metódus első paramétere referencia az entitásra(új elem, módosított elem, törölt elem), a második a művelet jellege. (add, delete, change) A folytatásban átmegyünk a kliensoldalra, és megnézzük, hogy Silverlight 2.0-ból, hogyan lehet egy ADO.NET DS szolgáltatást meghívni. ASP.NET Dynamic Data Intro (1.)Rohant már oda hozzád rendszergazda azzal, hogy extra gyorsan kéne egy webes felület az adatbázisához, ahol a tábláinak az adatait tudja felügyelt módon szerkesztgetni, feltölteni? Vagy netán neked fejlesztési időben jól jött volna egy ilyen felület, ahelyett, hogy a management studio-t használtad volna ilyen célra? Esetleg jól jött volna intranetes alkalmazásodhoz egy olyan out-of-the box site, ahol a tábláid adatai automatikusan vizualizálásra kerülnek, rögtön szerkeszthető formában? Ha igen, akkor a dynamic data a neked való technológia! Az ASP.NET Dynamic Data a .NET 3.5 SP1-ben került bemutatásra, és elsődleges célja, hogy egy adatmodell fölé épülve az entitásokat rugalmasan, testreszabható formában megjelenítse, és egyfajta CRUD-ot támogató felületet építsen fel dinamikusan. Ahelyett, hogy tovább elmélkednék a technológiáról, egy apró tutorial formájában inkább bemutatnám! Visual Studio 2008-ban, mikor egy új Dynamic Data Page-et szeretnénk készíteni az első döntés, amivel szemben találjuk magunkat, hogy Linq To Sql által készített adatréteget, vagy inkább az Entity Framework által generált adatréteget kívánjuk használni! A Dynamic Data Entities Web Application mögött ADO.NET Entity Framework dolgozik, míg a sima Dynamic Data Web Application mögött Linq To Sql. (Itt most az előbbit választom) A következő lépés az adat modell elkészítése, ami alapján a Dynamic Data felépítheti dinamikusan az oldalakat. A projekthez egy új elemet kell felvennünk, még pedig egy ADO.NET entity data model-t! 1. Az entitás modell elkészítéseA wizard-on kiválasztjuk, hogy adatbázisból generáljuk az entitás modellt, beállítjuk, hogy a Northwind adatbázishoz szeretnénk csatlakozni. A web.config-ba pedig elmentjük a hozzá tartozó connection stringet! A következő lépésben kiválasztjuk az összes táblát, így az Entity Framework az összes táblázhoz generál entitás osztályokat. A modell névtere a NorthwindEntities lesz. A studio legenerálja az entitás modelt, mentsük el, majd be is zárhatjuk! 2. A Dynamic Data Site életrekeltéseMiután a modell kész, valahogy el kell érnünk, hogy a Dynamic Data ezt a modellt használja fel adatforrásként. Ezt a Global.asax fájlban tehetjük meg. A fájlt megnyitva elénk tárol a statikus RegisterRoutes függvény. Ahogy az igen bő komment is mondja, szűntessük meg a kikommentezést az alábbi soron, adjuk meg az adatkontextus típusát, illetve a ScaffoldAllTables tulajdonságokat false-ről állítsuk true-ra. Ezzel két dolgot értünk el. Az egyik, hogy a dynamic data most már tudja, honnan gyűjtse be az adatokat, illetve, hogy default-ból, minden táblát vizualizálnia kell. (ScaffoldAllTables) Ezen a ponton futtathatjuk is az alkalmazásunkat! A szemünk elé tárul egy default témával ellátott oldal, ahol láthatjuk a tábláink listáját. Bármelyik tábla nevére klikkelve a hozzá tartozó adatok megjelennek. Íme a Products tábla: Kicsit nyaggatva, a következő dolgokat vesszük rajta észre...
Máris kaptunk egy csomó out-of-the-box funkcionalitást, pedig kódot még gyakorlatilag nem is írtunk. Az edit, illeve az insert linkre kattintva sem kell csalatkozunk. A Editnél a barátságtalan SupplierID helyett, egy dropdownlistből választhatjuk ki a Supplier nevét. Továbbá, mind update és mind insert esetén kapunk validátor vezérlőket, amik ellenőrzik az adatok helyességét. Például ha valami az adatbázis szinten "not null", nem maradhat kitöltetlenül, ahhoz kapunk RequiredFieldValidator-t! 4. Mi történik?Nem egyszerű kódgenerálásról van szó. Nagyon nem. A Solution Explorerben dalálható Dynamic Data mappára vessünk egy pillantást. Nyoma sincs Products, vagy bármilyen más adattábla specifikus oldalnak. Valójában PageTemplate-ek vannak.
Ha megnyitjuk az egyik ilyen aspx-et látjuk, hogy semmilyen tábla specifikus adat nincs benne, teljesen általános sablon dokumentumok. Sőt arra sem nagyon találunk semmi vonatkozást, hogy az egyes oszlopelemek hogyan jelennek meg. Ha futtatjuk az alkalmazást és egy pillantást vetünk az URL-re, igazán érdekes dolgot tapasztalhatunk. A Products táblára klikkelve az alábbi címet láthatjuk: Garantáltan nincs Products mappám, és a List.aspx sem tartalmaz semmi product specifikusat, az adat mégis helyesen jelenik meg. A fenti kódrészlet egy DynamicDataRoute-ot definiál, {table}/{action}.aspx formában, ahol az action lehet list, details, edit, és insert. Ez a kódrészlet határozza meg, hogy a Products/List.aspx értelmes URL legyen, és a products táblát tekintse aktuális entitásnak, amin a List.aspx sablon kerül alkalmazásra. Az engine elemzi a tábla adatait és a List.aspx-ben található sablon alapján megjeleníti az egyes adatokat, a típusuknak megfelelő usercontrolok alapján. Láthatjuk, hogy nem mezei kódgenerálás történik, hanem egy jóval dinamikusabb oldalösszeállítás. 3. Testreszabás, avagy mit lássunk és hogyan?Szép, szép, de hogyan lehet testre szabni? Hogyan zárhatok ki táblákat, oszlopokat a Scaffolding alól, ha nincs egy string, amit felül tudok definiálni, akkor hogy tudom megváltoztatni a nevét egy adott oszlopnak, vagy akár az egész táblának? Az entitás modellt kell módosítanom? A modell kiegészíthető metadatokkal, amelyek segítségével a dynamic data a megjelenítést módosíthatja. Az entitás osztályaink partial-ként vannak megjelölve, így egy külső class fájlban nyugodtan írhatok hozzá ezt-azt. A metadatok attribútumok formájában kapcsolódhatnak az entitásokhoz és a tulajdonságokhoz. Ezek az attribútumok a System.ComponentModel.DataAnnotations névtérben találhatók meg. (külön dll-ben szerepel, más technológiák is adaptálhatják) Nézzünk néhány egyszerű példát:
Na mára ennyi elég... :) Folyt. köv. |
|
|||
|
|