.Net Remoting, NHibernate ja generic kollektsioonid
06.01.2007 | GunnarÜks kena uusi tehnoloogiaid täis päev on jälle õhtus ja juurde on õpitud nii mõndagi uut ja huvitavat. Projektist endast, mis on see kõikse huvitavam, räägin siin veel arvatavasti selle kuu jooksul, aga praegu pühendugem ülaltoodud teemale, mis on täis imelisi üllatusi ja hulgemalt peavalu.
Ülesanne
Ülesanne siis selline, et vaja üle .Net Remotingu TCP kanalite liigutada objekte, mille loob NHibernate, mõni teine O/R mapper või mõni suvaline objekt. Teegid, milles asuvaid objekte liigutatakse, võivad olla kirjutatud kellegi kolmanda osapoole poolt. Tänu sellele, et remotingu mõlemas otsas peavad liigutatavad objektid teada olema ja meie neid ei tea täpselt, siis tuleb luua interface’id, mida kõik need objektid järgivad, sest muidu pole kliendil ja serveril nende objektidega suurt midagi teha. Palun, lahenda ülesanne!
.Net Remoting
Esimese asjana sai pandud tööle remoting kõige lihtsamal kujul. Valisime alustuseks kohe TCP kanalite kasutamise, sest netis ringi tuhlamine näitas, et SOAP põhistel kanalitel on omad piirangud generic kollektsioonidega, mille parandamisega Microsoft väga kiirustama ei kipu. Meil saab arvatavasti enne aeg otsa, kui neil. SOAP otsas on arendajatel probleeme esinenud IList<T> tüüpi kollektsioonidega, mille kasutamist ei tahaks kohe kuidagi piirata. Selle asemel saab muidugi edukalt kasutada ka ICollection<T> tüüpi kollektsioone, kuid ärgem siiski sildu põletagem.
Esimese hooga hakkaski kõik esmapilgul kenasti tööle. Objektid liikusid ja kõik tundus kena. Aga täpselt seni kaua kui tulid mängu teegid, mis tuleb dünaamiliselt külge võtta. Ma olen kindel, et siin on mingi konks, kuidas neid dünaamiliselt laadida, kuid selle nõksuni ma veel ei jõudnud. Plaanisin algul dünaamiliselt laaditavad teegid remotingu mõlemas otsas panna eraldi kataloogidesse, kust saaks neid reflectioni abil ohutult enne laadimist kontrollida, kuid siin läkski asi suht segaseks kätte.
Kui muidu saab reflectioni abil laadida erinevaid teeke ja luua objekte väga lihtsasti, siis remotingu ajas see täiesti segi. Objektid liikusid ainult serverist kliendile ja sedagi ainult nende meetodite kutsumise peale, millega mingeid parameetreid kaasas polnud.
Lahendus oli seekord lihtne. Teek, mis sisaldab vahetatavate objektide klasse, tuleb paigutada mõlemas pooles kataloogi, milles programm käivitub. Ainult siis loetakse need korrektselt külge. Internetis jagatud soovitustest langes Global Assembly Cache kohe ära ja paljud muud soovitused ei andnud ka soovitud tulemust.
Interface’ide teek oli mõlemal poolel juba kompileerimise ajal küljes ja sellest on korraga meil kasutuses ainult üks versioon. Selgus, et ka nendest third party teekidest peab mõlemas otsas vaatama vastu sama versioon ja see peab istuma kenasti programmi käivituva failiga samas kataloogis. Samasse kataloogi tuleb panna ka need teegid, millest third party teek mingeid klasse kasutab.
Muideks, erinevate versioonide korral tekib TCP kanalitel jälle uus mure. Nimelt ei leia .Net siis üles just seda teeki, milles deserialiseeritav klass asub. Kusjuures SOAP kanalitega seda jama pole. Siin on lahenduseks selline asi nagu “assembly binding redirection”, mis võimaldab siis ette öelda, et milline assembly mis juhul nö. asenduseks tuleb laadida. Või siis Global Assembly Cache.
NHibernate
Järgmine mure oli siis NHibernate ja kõiksugused vahvad klassid ja manager-klassid. Koos NHibernate’iga tulid loomulikult mängu tema enda kollektsioonid, millest ka kuidagi üle ega ümber ei saanud. Remotingu osas laabus asi suht lihtsasti - tuli lihtsalt NHibernate’i binary panna jällegi mõlemale programmile käivitumise kataloogi ja NHibernate oli kenasti küljes.
Probleem NHibernate’iga oli aga hoopis teine. Nimelt see, et generic kollektsioonide tugi sisseehitatud kujul puudub. Elu käib praeguse hetke viimases stable release’is, milleks on 1.0.3.0, endiselt IListide peal. Meil oli aga püstitatud nõue, et kasutaksime generic kollektsioone.
Lahendus sellele probleemile siiski tuli. Nimelt on olemas NHibernate’i jaoks spetsiaalne contribution nimega NHibernate Generics. Täpsemat infot pakub Ayende @ Rahieni lehekülg Generics. Paraja pikkusega päris põhjalik tekst annab väga hea ülevaate generic kollektsioonide kasutamisest NHibernate’iga ning lehelt saab tõmmata alla NHibernate’i jaoks vastava teegi.
Lühidalt peitub siis lahendus selles, et on loodud EntitySet<T>, EntityList<T> jms klassid, mille põhjal saab luua oma kollektsioonid. Overheadi nende klasside kasutamine oluliselt ei lisa, sest antud kollektsioonid ei tegele value type’idega. Igal juhul sain ma selle artikli abil kenasti NHibernate’i ja generic kollektsioonid tööle. Muideks, eeltoodud lehekülg toob välja ühe päris hea nõksu kuidas enda tehtud klassides NHibernate’i poolt täidetavaid kollektsioone kenasti up-to-date hoida. Ja kõik jäi esmasel kujul toimima ka üle remotingu.
Kokkuvõtteks
Et kõik see värk käima saada, oli paras tegemine, kuid see-eest igati õpetlik. Selge see, et debugimist ja optimeerimist on veel üksjagu vaja teha, kuid see kõikse põhilisem toimib. Samas pole antud lahendus just selles osas elegantne, et kasutada tuleb selliseid suht kahtlaseid asju nagu pooltoores remoting ja mingi lisavidinaga NHibernate. Kuid loodetavasti järgmised versioonist neist mõlemast on juba tublimad ja võimsamad. Ja mis meile kõige parem - me oleme ka ise ühe kogemuse võrra kirjumad jälle

07.07.2007 kell 02:14
Tundub, et olete tubli töö ära teinud. Väga hariv.
Olen ka ise Generics kasutamisega pead valutanud. Probleem oli kahe parameetriga generic kasutamisel SomeClass(T,TKey) kui üks parameetritest oli enum.
Remoting on üldse eraldi teema. Süsteem peab väga läbimõeldud olema andmevahetusprotsessi terviklikkuse osas. Remoting läheb ilusti tööle, aga tekib igasugu efekte näiteks võrgu tõrgete korral. Mõnel tuleb tahtmine ka kriitilisi asju wifi võrku panna…
07.07.2007 kell 16:41
Tänan hea sõna eest, Toomas
See tegemine oli just selline DT delikatess - paras ajude ragistamine, suht tume maa ja küllaltki keerukas teema. Peamine, mis me tahtsime saavutada, oli see, et saaksime kasutada erinevaid implementatsioone objektidest ning liigutada neid üle remoting channelite. See sai ka saavutatud, sest NHibernate oli vaid üks objektide loomise run-time. Ma kirjutasin talle kõrvale ka teise run-time-i, mis töötas puhtalt ADO.NET peal ning kasutas teisi objekte. Samas mõlema teegi objektid implementeerisid samu interface-e.
Seega saime ühe väikse eksperimendiga päris mitu korda targemaks. Remotingu teises otsas istus, muide, .Net peal kirjutatud MMC konsooli rakendus, mis oli veel omaette teema.