Archívum

Archive for 2009. január

Lusta és Mohó kiértékelés ADO.NET Data Services-ben

január 4, 2009 Hozzászólás

Egy 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.
Pl: theCustomer.Orders, vagy theCustomer.Orders[0].Order_Details és így tovább. Mivel egy Rest alapú adatszolgáltatásról van szó így ezt url alapú lekérdezésekkel érhetjük el a következő módon:

ALFKI ID-val rendelkező felhasználó
http://localhost:6978/NorthwindDataService.svc/Customers(‘ALFKI’)

A hozzá tartozó összes megrendelés
http://localhost:6978/NorthwindDataService.svc/Customers(‘ALFKI’)/Orders

A hozzá tartozó egyik megrendelés. Kézen fekvő lenne, de ilyet sajnos nem lehet…
http://localhost:6978/NorthwindDataService.svc/Customers(‘ALFKI’)/Orders(10643)

(A vásárló adott megrendeléséhez tartozó részletek) Innentől kezdve ez sem megy…
http://localhost:6978/NorthwindDataService.svc/Customers(‘ALFKI’)/Orders(10643)/Order_Details

A feladat egy pontig az előző gondolkodás nyomán megodható: A várásló egy adott megrendlése
http://localhost:6978/NorthwindDataService.svc/Customers(‘ALFKI’)/Orders?$filter=OrderID eq 10643

Azután a megrendléshez tartozó részletek
http://localhost:6978/NorthwindDataService.svc/Orders(10643)
http://localhost:6978/NorthwindDataService.svc/Orders(10643)/Order_Details

Azonban az egész megoldható egyetlen lépésben is, ún. eager loading (vagy mohó kiértékelés) segítéségével
http://localhost:6978/NorthwindDataService.svc/Customers(‘ALFKI’)/Orders?$expand=Order_Details

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
http://localhost:6978/NorthwindDataService.svc/Customers?$expand=Orders

Az összes megrendlőhöz tartozó összes megrendenlés és azok részletei
http://localhost:6978/NorthwindDataService.svc/Customers?$expand=Orders/Order_Details

Az összes megrendlőhöz tartozó összes megrendenlés és azok részletei, valamint a megrendlésekhez tartozó szállítók
http://localhost:6978/NorthwindDataService.svc/Customers?$expand=Orders/Order_Details,Orders/Shippers
(ezen a ponton kezd izzadni az IE, szóval csak óvatosan 🙂 )

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:

DataService Demo Application

Kategóriák:Uncategorized