Zoltán 的个人资料Árvai Zoltán Blogja日志列表 工具 帮助

日志


11月27日

ADO.NET Data Services Introduction

Az ADO.NET DS célja és szerepe

Fejleszté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ítse

Az 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.

image

Megnyitva a NorthwindDataSerivce.cs-t, a következő látvány tárul elénk:

image 
DataService<T> ősosztályból származtatunk, ahol a T generikus paraméter az adatmodell típusa kell legyen. Jelen esetben NorthwindEntities. Cseréljük is ki a fenti sort, erre:

image 

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:

image
(Amennyiben a tábla név helyére *-ot írunk, az összes táblára érvényes lesz az adott EntitySetRight-s beállítása)

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.
Most futtatva az alkalmazásunk, máris láthatjuk a kipublikált két entitáshalmazt.

image

Az ADO.NET DS használatba vétele

Fentebb 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ő:

http://host/<service>/<EntitySet>[(<Key>)[/<NavigationProperty>[(<Key>)/...]]]

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.

Néhány példa, amit érdemes kipróbálni:

NorthwindService.svc/Customers(’ALFKI’)
NorthwindService.svc/Customers('ALFKI')/Address
NorthwindService.svc/Customers('ALFKI')/Address/$value
NorthwindService.svc/Customers?$orderby=City
NorthwindService.svc/Customers?$orderby=City desc
NorthwindService.svc/Customers?$filter=City eq ’London’
NorthwindService.svc/Customers?$filter=City eq 'London'&$orderby=ContactName
NorthwindService.svc/Customers('ALFKI')/Orders

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.

image

Akárcsak az entitáshalmazokat, a ServiceOperation-t is engedélyezni kell:

image

Ezt követően a metódus hívható az alábbi módon:
http://localhost:6627/WebSite15/NorthwindDataService.svc/GetProductsByCategory?categoryName='Beverages'
Több paramétert "&"-el elválasztva lehet felsorolni.

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.

image 

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.

image

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!

image

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ése

image

A 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!

image

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.

image

A studio legenerálja az entitás modelt, mentsük el, majd be is zárhatjuk!

2. A Dynamic Data Site életrekeltése

Miutá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)

image

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.

image

Bármelyik tábla nevére klikkelve a hozzá tartozó adatok megjelennek. Íme a Products tábla:

image

Kicsit nyaggatva, a következő dolgokat vesszük rajta észre...

  1. A Lista szűrhető a fenti dropdownlist-ek alapján
  2. Az oszlop fejlécére klikkelve rendezésre kerül a lista
  3. Van edit, delete és details funkcionalitás
  4. Új elem beszúrására találunk link-et
  5. Szabályozható futásidőben, hogy hány elemet jelenítsünk meg egy oldalon
  6. A Categories szekcióban CategoryID helyett, a kategória neve jelenik meg!!!
  7. Minden postback aszinkron, azaz az oldalunk AJAX-et használ.
  8. A Products táblából az asszociációkon keresztül könnyedén átnavigálhatunk egy másik táblára, pontosabban a másik táblában található, az adott sorhoz kapcsolódó adatokhoz.

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!

image

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.

image

Nyoma sincs Products, vagy bármilyen más adattábla specifikus oldalnak. Valójában PageTemplate-ek vannak.

  1. List.aspx - megjelenítés
  2. Edit.aspx - szerkesztés
  3. Insert.aspx - beszúrás
  4. Details.aspx - adott sor részleteinek megjelenítése
  5. ListDetails.aspx - Inline szerkesztés

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.
Erre szolgálnak segítségül a FieldTemplate-ek. Itt az egyes típusokhoz tartozik egy-egy UserControl. Mind a megjelenítéséhez, mind az edit állapotához. Például a Boolean.ascx-ben egy egyszerű checkbox van. Azaz az egyes típusoknak egy UserControl felel meg, amit mi testreszabhatunk itt, vagy kicserélhetünk teljesen, netán új típusokat vezethetünk be.

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:

image

Garantáltan nincs Products mappám, és a List.aspx sem tartalmaz semmi product specifikusat, az adat mégis helyesen jelenik meg.
Az egészért az ASP.NET Routing felelős. A Global.asax-be visszavándorolva a következő kódrészleteket találhatjuk még:

image

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:

  1. A Territories tábla kizárása a megjelenítésből. (Add new class: Territories.cs)
    Az entitás osztály fölé helyezve a ScaffoldTableAttribute-ot, false értéket átadva neki, kizárhatjuk a táblát a vizualizációból.

    image 
  2. Oszlop kizárása - Products táblából a ProductID kizárása
    Ha tagokra kívánunk hivatkozni akkor szükségünk lesz egy osztályra, ami az entitások tulajdonságaival kapcsolatos metaadatokat írja le. Ezt követően az entitás osztályon jelezni kell, hogy melyik osztály tárolja a metaadatokat. Ezért definiálunk egy belső metadataosztályt, ahol az egyes property-kre hivatkozhatunk. A property típusa lehet object, ez a dynamic data-t nem érdekli. Mivel a ProductID-t zárnánk ki, ezért a ScaffoldColumn attribútumot állítjuk false-ra a ProductID property-n.

    image
  3. Oszlop átnevezése - UnitPrice oszlop átnevezése Price mezőre
    A metadata osztályban a UnitPrice mező elé a DisplayName attribútumot vesszük fel, ahol paraméterként megadjuk, hogy mostantól az mező neve mindenhol Price legyen.

    image
  4. Tábla átnevezése - Products tábla átnevezése Termékek táblára.
    A változás entitás színtű, így a Products Entitás osztályunkat értinti a DisplayName attribútum.

    image
  5. Értékek formázása - UnitPrice oszlopban található értékek megjelölése pénznemként
    A ProductMetadat osztályban a UnitPrice property fölé elhelyezzük a DisplayFormat attribútumot, a DataFormatString paramétere pedig legyen a {0:C} formázó string.

    image
  6. Dátum formázása
    A dátumok még az óra, másodperc információt is tárolják. Az Orders táblára klikkelve látjuk, hogy több oszlopban is szerepel ez a probléma. A megoldás nem az, hogy egyenként változatjuk meg a propertyk DisplayFormat-ját, hanem a típushoz (DateTime) tartozó usercontrol-on változtatunk. Ha megnyitjuk a DateTime.ascx-et a következőt látjuk. A FieldValueString tárolja a dátumot string formátumban.
    image 
    Ezt kell lecserélnünk az alábbira.

    image

    Így az alkalmazásunkban az összes dátum formázásra került, hiszen mind ezt a usercontrolt fogja használni.

Na mára ennyi elég... :) Folyt. köv.
A következő cikkben validáció testreszabásáról, illetve Custom Page-ekről (oldalak egyéni testreszabásáról) lesz szó.