joi, august 23, 2007

using NHibernate;

Am început să folosesc NHibernate pentru persistenţa obiectelor în baza de date. Toate bune şi frumoase, merge ca uns, cum s-ar zice. Dar iată că am dat peste o problemă..poate aveţi experienţă şi-mi puteţi împărtaşi mai mutle detalii.

Am o aplicaţie web, un proiect cu unit tests (! nu ştiu cum să le traduc în RO). În momentul în care se termină request-ul web şi am făcut câteva modificări la obiectele din aplicaţie, închid sesiunea NHibernate, apelând întâi un session.Flush(), astfel încât toate schimbările care încă nu au fost trimise în baza de date să fie procesate.

Aplicaţia cu teste automate (iaka am tradus altfel :)), are două metode care se execută înaintea şi după finalizarea testului. În partea de iniţializare creez o nouă tranzacţie şi configurez sistemul (indirect, baza de date) la o anumită stare, considerată stare iniţiala pentru teste. În partea de Cleanup (a doua metodă de care vă vorbeam) închid sesiunea şi fac rollback la tranzacţie, astfel încât baza de date să ramană curată :). Unde e problema, nu?

Deci, pentru un test se execută:
1. Initialize();
2. MyTest();
3. Cleanup();

Deoarece la sfârşit de Initialize nu execut Flush pentru sesiunea NHibernate curentă, starea sistemului la care mă aştept nu este ceea dorită. Soluţia a fost să execut manual Flush, dupa care a mers. Dar eu mă întreb, cum ar fi mai bine să fie în cadrul aplicaţiei Web? Să execut Flush atunci când se termină requestul (end of a unit of work), sau după fiecare operaţie de update a obiectelor (ceea ce nu merge..).

De unde am plecat:

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.Collections.Hashtable.HashtableEnumerator.MoveNext()
at NHibernate.Impl.Printer.ToString(IEnumerator enumerator)
at NHibernate.Impl.SessionImpl.FlushEverything()
at NHibernate.Impl.SessionImpl.Flush()

Any ideas?

3 comentarii:

CISCBrain spunea...

Nu stiu daca e o regula de baza la cum sa dai Flush la sesiune, dar daca creezi un obiect nou cred ca esti obligat sa dai save manual. Sesiunea nu o sa stie de el si nici nu stiu cum poti sa-l atasezi sesiunii, din nou, manual.

In alta ordine de idei, pentru pastrarea curata a bazei dupa teste in MBUnit poti marca testele cu [Rollback] si o sa se ocupe el de restul. Poti vedea mai multe detalii aici: Rollback Attribute. Oricum sugerez ca macar sa te uiti peste MBUnit. Poti trece instant de la NUnit dar ai o caruta de functionalitate in plus: Row Testing, Combinatorial Testing, Test Sequences...

sharpoverride spunea...

Ok in primul rand ce faci tu nu e Unit Testing :P

Afla de ce aici http://haacked.com/archive/2008/07/22/unit-test-boundaries.aspx

Gabriel Enea spunea...

Thx,

Poate n-am fost clar, dar in testul propriu-zis nu apelez direct baza de date, ci doar operatii din BLL (entitati, manageri). In schimb, pentru a simula modul de utilizare a BLL-ului intr-un context Web, am nevoie sa deschid/inchid indirect contextul NHibernate care sta la baza DAL-ului.

Nu se apeleaza NHibernate in mod direct, ci se foloseseste un wrapper peste acesta.

Deci?