Ristiretk MS Entity Framework pühale maale

Nüüdseks umbes kaks aastat tagasi kirjutasin ühe programmi, millega saab genereerida SQL 2005 andmebaasi struktuurist C# klasse ehk vaese mehe ORM. Hiljem lisandusid võimalused genereerida neid olemeid manipuleerivad meetodid a la SaveEntity, DeleteEntity, FillEntityList jms. Samuti tekkisid SQL Compactiga sünkroniseerimist võimaldavad veebiteenused.

Ma ei pidanud seda rakendust kunagi täiemahuliste kommerts-ORMide konkurendiks, selle eeliseks oli aga täielik kontroll koodi üle mida see genereeris. Samuti genereeris see koodi, mis vastas täpselt just WinForms rakenduste vajadustele, mida paljud kommerts-ORMidki kahe silma vahele olid jätnud, näiteks OnChanged sündmused andmesidumise võimaldamiseks. Sestap ei pühendanud selle arendamisele enam aega kui hädavajalik.

Juba Teched 2007-l Hispaanias 2007 aasta novembris jagas MS laiali ajakirju, kus tutvustati kohe-kohe saabuvat MS Entity Frameworki, mis promo järgi pidi olema võimsaim ORM ever. See oli teine põhjus, miks ma ei arendanud enda töövahendit eriti tugevalt edasi.

See oli sissejuhatus, edasi tuleb aga ristiretke kirjeldus, mis lõpeb sellega, et maandusin esialgu jällegi “vana hea” ise genereeritud ORMi otsa ja uus ristiretk on kavas aastal 2009.

Esimene: kodust minema!

Projekteerides anno 2008 uut suuremat rakendust, tekkis küsimus, kas saaks ehk optimeerida klassikalise veebiteenustel töötava WinForms rakenduse arendustsüklit andmebaas->C# olemid->tüpiseeritud ASP.NET veebiteenus->SCSF andmeteenus->C# klient-olemid. Kuigi mul oli käepärast seesama enda arendatud ORM tööriist, suunasin otsingud kahele peamisele suunale.

Teine: SQL 2005 HTTP Endpoints ehk SQL 2005 veebiteenused

SQL 2005 server sisaldab võimalust näidata protseduure, funktsioone ja päringuid otse täiemahuliseks SOAP veebiteenuseks, kus ka WSDL kaasas. Seega kujuneks veebiteenuse kasutamine äärmiselt lihtsaks – kirjutada protseduur -> avada HTTP endpoint -> genereerida rakenduses wsdlist proxy klassid. Esiteks, elimineeritud oleks vajadus üleüldse mingit veebiteenuse koodi kirjutada (HTTP endpoint avamine on lihtne ja selle tekitamise saab lisada näiteks andmebaasi paigaldamise koodi, kuna on tavaline T-SQL).

Kolmas: WSDL tupiktee – olemite ühesuunaline liiklus

Loomulikult aga soovisin ma veebiteenust panna välja näitama mitte primitiivtüüpe vaid objekte, mida saaks tarbida otse või läbi vahekonverteri (mida saaks vähemalt automaatselt genereerida) normaalses OOP rakenduses. HTTP endpointe kasutades on see isegi teoreetiliselt võimalik: 1) deklaratsioonis saab ette anda ise genereeritud WSDLi, mis võib olla milline iganes. 2) veebiteenus ise ehk protseduur, mida see eksponeerib võib tagastada näiteks SQL serveri XML andmetüübi. Siit oleks vaid üks lühike samm rakenduseni. Siiski nõuab kokkuvõttes eriti just “tavapärase” tüpiseeritud veebiteenusega analoogse WSDLi moodustamine tuntavat pingutust, sest kuigi “for XML” päringud saab panna väljastama ka mitte tulemust, vaid XML schemat, on saadud XML schema vaid päringu tulemuse kirjeldus primitiividena, mitte aga mingi olemina. Ok, teostatav. Kuid veel suurem takistus ilmneb olemite tagastamisel ehk näiteks SaveEntity(Entity x) loomisel. Kui väljastamisel oli teoreetiliselt võimalik veel vajalik WSDL saavutada (vihje: XML andmetüüp sproc väljundina – ärge küsige minult näitekoodi, viskasin lõpuks kõik minema ja katsefaile otsida ei viitsi), siis nn. tagasisalvestamisel oleks vastupidine trikk kindlalt hambad murdnud (siiski võimalik – XML andmetüüp -> SQL 2005 XML XQUERY sproc muutujateks -> sproc salvestamine.) Kokku aga siiski juba nii palju lisatööd, et muudab mängu mõttetuks. Ehk, kogu HTTP endpointide ilu  rikkus ära just salvestusoperatsioonide keerukus.

Ja lõpuks leidsin MS blogidest vihje, et SQL 2008-s on HTTP Endpointid nagunii “deprecated” ja asendatakse peagi ADO.NET Data Services tehnoloogiaga, mis töötab ADO.NET Entity Framework peal ja on veelgi vingem. Otsustasin, et uue projekti jaoks ei ole ilmselt mõistlik kasutada tehnoloogiat, mis on järgmises versioonis “vananenud”. Muidugi, kuni lõpeb 2005 tugi kuluks veel aastaid ja ilmselt sureks ka uus rakendus enne kui SQL 2005, tekitab probleeme hoopis paanika, mis arendajate seas tekib kohe kui MS teatab millegi “deprekeerimisest” – HTTP endpointide kohta leiab niigi vähe infot, ja ilmselt seda juurde ka ei tule kuna tehnoloogia on kuulutatud kaduvaks.

Kokkuvõte – SQL 2005 endpoints on hea vahend näidata välja ad hoc andmeid ilma massiivse veebiteenuse koodi kirjutamiseta. Näiteks tarbimiseks Excelis vms. kus peamiselt liiguvad andmed ühes suunas. Kuid klassikalise OOP CRUD süsteemi jaoks on kõlbmatu.

Neljas: ADO.NET Data Services tõotatud maa

ADO.NET Data Services on just jõudnud koodnimest Astoria viimase beetani, ametlik ADO.NET Data Services ja Entity Framework peaks valmis saama selleks sügiseks ehk täpselt õigeks ajaks. Hetkel (juuni 2008) väljas olevas .NET 3.5 SP1 beetas ilmselt ei muutu enam dramaatiliselt midagi peale bugfixide (nagu MS projekti tiim kirjutab, vt. Astoria Team Blog). Seega väga sobiv kandidaat – uus, värske, peagi valmis ja ilmselt niipea ei “vanane”.

Esimeste olemite genereerimine baasist, nende eksponeerimine veebiteenustes kulges suuremate tõrgeteta ja tuleb tunnistada, et võrreldes SQL 2005 HTTP endpointidega on OOP CRUD väga lihtne. Hoolimata sellest, et süsteem tuleb esialgu paigutada ASP.NET veebiteenusesse, käib kogu vajaliku koodi genereerimine automaatselt ja seega võib selle pidada hallatavuselt ekvivalentseks HTTP endpointidega hoolimata rakenduse asumisest kahel platvormil.

Esimene tõrge tekkis aga protseduuride välja näitamisel. Nimelt peale esimest kahte triviaalset tabelit oli vaja kolmanda sammuna näidata andmeid, mille arvutamine sprocina oli ainumõeldav. Tegu oli teatava ajagraafiku arvutamisega, kus lähteandmeteks on loetelu sündmuste toimumisest – kas üksikute kuupäevadena või siis korduvusreeglitena, millest tuli lahutada nn. erandkuupäevad. Kasutajale ja ka andmebaasile on kõige sõbralikum sellist infot hoida kahes tabelis – graafik ise (sh. korduvused kujul intervalli algus, intervalli lõpp, korduvusreegel) ja erandid (mis lülitasid korduvusreeglist teatud kuupäevad välja – näiteks “igal nädalal teisipäeviti, välja arvatud 23. juuni”). Nende pealt aga tuleb graafikus kuvamiseks komponeerida lõplik graafik. Muidugi, seda saaks teha ka veebiteenuses otse ja C# koodis oleks see triviaalne. Kuid probleemiks oli see, et graafikut vajavad ka moodulid, mis ei tarvita veebiteenust. Seega oleks äriloogika paiknenud mitmes kohas.

Seega oli vaja kuvada ADO.NET Data Services abil välja sproci andmed. Ma arvasin, et see on triviaalne, sest a) esialgne Astoria manuaal kirjeldas seda b) minu jaoks oli ilmne, et maailmas, kus suur osa andmete ligipääsu on teostatud sprocidena (seda on ju jutlustatud aastaid), ei saa ADO.NET Data Services ilma selleta läbi.

Tõde oli aga kohutav – ADO.NET Entity Framework v.1.0 ei toeta nn. read sproce ehk sproc->ADO.NET Entity->teenus. Sprocidel on koht vaid CUD (Create, Update, Delete) maatriksis, kus on võimalik andmete manipuleerimisse sekkuda.

Erinevalt HTTP Endpointide tupikust, kust (kuna lubati uut võimsamat Entity Frameworki) kiiresti tagasi pöördusin, tegin siin mitmeid kiireid otsinguid probleemi lahendamiseks.

1. suvaline sproc on võimalik kenasti importida EF (Entity Framework) Function Impordiks ja seda saab otse [WebGet] või [WebInvoke] meetodis kasutada nii, et ei ole vaja minna veel Data Services tehnoloogiat mõttetuks muutva SQL kirjutamiseni. Kuid, IEnumerable [WebGet]  meetodi kutsub EF kliendi osa välja kujul WebGetMethodName()?args isegi juhul, kui anda sellele parameetrid. Jah, eemaldades () sulud väljakutses (näiteks kirjutades päringu otse brauserisse või siis käsitsi koostades päringu urli, mitte dataContext.AddParameter) kõik töötab – kuid vaid seni, kuni MS otsustab selle “augu” likvideerida RTMis. Seega oleks oma koodi sellele häkile üles ehitada ohtlik.

2. WebGeti kirjutamine IQueryablet tagastavaks nõuab klassi koostamist, mis implementeerib IQueryable. Sproci wrapper seda ei ole. Lisaks ilmusid blogidesse ja foorumitesse teated, et “returning primitive types as IQueryable<type> is not supported” – anymore. Seega ei maksaks ehitada üleüldse oma lootusi seda RTMis näha, isegi kui ehitaks valmis IQueryable objekti sproc tulemuse peale (mida ma ka isegi tegin).

3. Well, edasi on võimalus luua “legaalne” EF olem, mida IQueryable Webget probleemideta väljastab ja ADO.NET Data Services klient ka korrektselt tarbib. Kuid, ärge lootkegi et saate EF olemi väljastada sproci tulemusena. Ehk, lugege edasist nagu anekdooti, kus puänt on viimasel real:

a) Olemi (Entity) loomine on täiesti ok ja loomulikult legaalne tegevus

b) sprocist, mis tagastab täpselt olemile vastava signatuuriga andmed, saab legaalsete vahenditega luua Function Impordi. Disaineris saab sproc tagastatavaks andmetüübiks valida loodud olemi (sic!). Kuid sellega ei ole halligi edasi teha. Loe edasi …

c) VS keeldub rakendust kompileerimast, sest “entity is not mapped”. Nimelt, iga olem peab olema mäpitud _tabelile_ (WTF see sproci andmetüübi määramine siis üldse on??).

d) Nüüd algab nali. MS EF tiimi liikmed annavad foorumites soovitusi, mida kadestaks isegi Dogbert Dilberti koomiksites. Jah, .edmx xml failidesse saab tekitada “virtuaalsed” tabelid käsitsi XMLi tehes, millest omakorda saab mäppida väljad sproci väljundisse – ja kõik töötab. Usinamad tiimiliikmed on teinud lausa tutorialid, mõned lausa edmx faili “tweakimise” utiliidid. Kuid puänt on selles, et kogu “lisatud” kood lendab vastu taevast kohe, kui te soovite lisada või uuendada mõnda “legaalset” tabelit andmebaasist. MS koodi genereerimise utiliit mitte ei uuenda vastavaid lõike koodis vaid genereerib kogu koodi uuesti. Ja kogu töö on vastu taevast. Tunnen kaasa neile õnnetutele foorumi külastajatele, kes kurdavad isegi kuni kahest kuust raisatud ajast, kuni jõuti sproci teemani.

Ma ei suuda uskuda, kuidas on võimalik et MS ignoreeris algusest peale seda ilmset fakti, et de facto on suur osa andmeloogikat ehitatud sprocidesse ja mitte ainult CUD loogika.

ADO.NET Data Services Program Manager aga tunnistab lõpuks tehtud jama ja ütleb, et “read” sprocide tugi tuleb Entity Framework v. 2-te. Ja millal tuleb Entity Framework v. 2? “somewhere in 2009″. Seniks aga kasutage MS poolt kiiresti kokku häkitud “EF Extensione” ehk C# koodi millega saab EF-ist ikkagi sprocide andmeid kätte. Edu neile, kes julgevad kasutada selliseid häkke, mille MS võib iga hetk aknast välja visata.

Viies: tagasi koju, püha Graal jäi leidmata

Kokkuvõte – ADO.NET Data Services ja Entity Framework on kahtlemata suurepärane ORM ja triviaalse CRUD koodi kirjutamine võrreldes HTTP endpointidega või mõne muu ORMiga on mugav ja kiire. Kuid selles haigutab meeletu auk kohas, kus andmed ei paikne lihttabelites. Samuti teeb ettevaatlikuks kogu projekti kestel mitu korda muutunud reeglid veebiteenuste tarbmisel. Seega lükkan EF kasutamise edasi aastasse 2009 EF 2.0-ni (kui selleks ajaks see projekt veel elab) ja kasutan end tõestanud kommerts-ORMe või siis oma vana head kodukootud rakendust.

Epiloog: kodumaja on alles

Siiski, kuna lõin EF-t tarvitava rakenduse SCSF ja OOP klassikalisi reegleid järgides, kulus EF->enda ORM modifikatsiooniks vaid paar loetud tundi. Kuna rakendus oli arhitektuurselt jaotatud MVP mustriks, kus teenus kasutas ADO.NET Data Services teenuseid läbi EF Data Contect peale loodud wrapperite, pidin muutma vaid reaalselt EF-i päringuid tegeva koodi SOAP WS päringuid saatvaks koodiks. Kõik muu töötas edasi. Fantastiline.

Lisa kommentaar

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Muuda )

Twitter picture

You are commenting using your Twitter account. Log Out / Muuda )

Facebook photo

You are commenting using your Facebook account. Log Out / Muuda )

Connecting to %s


Follow

Get every new post delivered to your Inbox.