Ho strutturato la UI della mia applicazione in questo modo:
Ci sono dei pulsanti che corrispondono a delle azioni tipo New, Open, Save, Cancel, Edit, Delete, Copy, cosa facciano penso si capisca. Uso WPF con MVVM.
Per la persistenza uso NHibernate, di conseguenza quando creo il ViewModel, creo anche la session, dovrebbe essere l'approccio corrispondente al pattern Open Session in View.
Passiamo ora alle funzionalità.
Il comando open serve per caricare un oggetto e visualizzarlo, successivamente l'utente decide cosa farne (modificarlo, cancellarlo, copiarlo in un nuovo oggetto).
Nel caso di modifca, tramite il comando Edit, vengono abilitati i controlli sulla window per permetterne la modifica. A questo punto l'utente può decidere di salvare le modifiche (comando Save) oppure annullare (comando Cancel).
Se decide di salvare, tramite la session con cui ho letto l'oggetto, chiamo SaveOrUpdate e fin qui fila tutto lisco, finita l'operazione i controlli ritornano ad essere readonly e si ricomincia il ciclo.
Il problema nasce nel momento in cui, dopo aver modificato l'oggetto, l'utente decide di non salvare, ma chiama il metodo Cancel. Succede che quanto visualizzato a video non torna com'era all'origine, anche se prima di permettere la modifica faccio una copià della entity e la ripristino dopo aver annullato la modifica.
Succede anche che, se richiedo di nuovo la lettura dell'oggetto tramite la stessa session, vedo nella output window che NH genera la select verso il db, ma ho l'impressione che comunque non venga eseguita, in quanto continuo a vedere a video la stessa situazione di prima con le modifiche non annullate.
Riesco a risolvere il problema facendo una dispose della session e ricreando la session nel metodo Cancel, solo che a questo punto ho perso tutti i vantaggi di operare tramite la session con la quale avevo letto i dati, in quanto una successiva modifica e salvataggio comporta comunque il salvataggio di tutte le entità collegate in una eventuale collection di dettagli, anche se questi non hanno subito modifiche, a causa del fatto che NH perde traccia dello stato della entità.
Qual'è l'approccio corretto da usare per gestire questi use-case?
Mi chiedo se non sia più conveniente cambiare approccio, facendo decidere prima all'utente cosa vuole fare (voglio visualizzare, modificare, cancellare, stampare, copiare, ecc.), conseguentemente creo la session, leggo i dati, salvo o annullo l'operazione e alla fine distruggo la session, anche se l'approccio precedente mi sembrava più valido, in quando l'utente decideva cosa fare solo dopo aver visualizzato l'oggetto.
Antonio Budano
Ci sono alcune cose che mi lasciano perplesso. La prima è questa, se tu carichi un oggetto con la sessione, lasci la sessione attiva, e permetti all'utente di modificarlo, non hai bisogno di chiamare SaveOrUpdate, le modifiche potrebbero essere propagate in qualsiasi momento (a meno che non lasci il flushmode a never).
Se vuoi gestire correttamente il cancel, la cosa migliore è implementare l'IEditableObject, in questo modo l'oggetto ripristina i suoi valori correttamente.
AntB:Succede anche che, se richiedo di nuovo la lettura dell'oggetto tramite la stessa session, vedo nella output window che NH genera la select verso il db, ma ho l'impressione che comunque non venga eseguita, in quanto continuo a vedere a video la stessa situazione di prima con le modifiche non annullate.
Se tu hai caricato un oggetto, lo modifichi, poi esegui un session.get di quello stesso oggetto è chiaro che le sue proprietà non cambiano, dato che l'identity map fa si che ti venga tornato esattamente lo stesso oggetto. Fino a che una sessione non viene disposata oppure non chiami session.evict, l'oggetto è nello stato persistente e tale rimane.
AntB:Riesco a risolvere il problema facendo una dispose della session e ricreando la session nel metodo Cancel, solo che a questo punto ho perso tutti i vantaggi di operare tramite la session con la quale avevo letto i dati, in quanto una successiva modifica e salvataggio comporta comunque il salvataggio di tutte le entità collegate in una eventuale collection di dettagli, anche se questi non hanno subito modifiche, a causa del fatto che NH perde traccia dello stato della entità.
Chiaro, se fai il dispose ricarichi poi il valore dell'entità nel db, quindi perdi le modifiche effettive fatte dall'utente, il fatto che il salvataggio ti comporti il salvataggio a catena dipende dal cascade che hai impostato nei mapping. Sicuramente se il cascade è di tipo update lui rifa l'update di tutto, quando riattacchi l'oggetto alla sessione.
Una cosa che puoi fare invece di chiduere tutta la sessione è fare l'evict del solo oggetto che vuoi ricaricare e poi rifare un get, cosi ricarichi solo quello, alternativametne puoi fare un session.Refresh(entità) anche se originariamente non è stato pensato per quello che vuoi fare tu. La soluzione migliore è usare la IEditableObject.
alk.
--Blog Eng: http://www.codewrecks.com/blogBlog Ita: http://blogs.ugidotnet.org/rgmTwitter: http://twitter.com/alkampfer
Nella session ho impostato flushmode = commit, quando devo salvare l'oggetto inizio un transazione, chiamo saveorupdate e faccio la commit della transazione. Dici che non serve chiamare SaveOrUpdate?
Per quanto riguarda le modifiche, avevo usato IEditableObject ma mi dava problemi con XceedDataGrid e l'avevo commentato, provo a riabilitarlo e vedo cosa succede.
Il fatto è questo supponi questo pseudocodice
Customer c = Session.get<Customer>(idcustomer);
fai qualche cosa con c, tipo modifica il nome
IList<Customr> clist = Session.CreateQuery("select C from Customer C where ..... ");
nel momento in cui fai la select con la query ti salva l'oggetto customer a cui hai cambiato il nome, questo chiaramente dipende da in che modo hai settato il flushmode. Se tu usi il Flushmode commit chiaramente ti flusha solo prima della commit ella transazione ma attenzione perchè come dice la doc.
FlushMode.Commit—Specifies that the session won’t be flushed before queryexecution (it will be flushed only at the end of the database transaction). Beaware that this setting may expose you to stale data: modifications you made toobjects only in memory may conflict with the results of the query.
Se sei consapevole di questo allora puoi andre tranquillo, chiaramente però se tu fai un
modifichi qualsiasi cosa di c
la seconda query ti torna esattamente lo stesso oggetto in memoria della prima, questo perchè hai la idmap.
Se non puoi proprio utilizzare IEditableObject (mi pare strano), quello che puoi fare è
1) Devi permettere all'utente di editare l'entità X2) Session.Detatch di X3) Se l'utente salva Session.Update4)Se l'utente preme cancel Session.load e la ricarichi.
Cosi dovrebbe andare abbastanza bene e risolvere i tuoi problemi.
Implementando IEditableObject, devo chiamare io manualemente BeginEdit quando l'utente inizia la modifica, EndEdit quando prima di fare il salvataggio e CancelEdit quando vuole annullare?
Forse sto facendo confusione, mi pareva che mettendo in binding l'entità con i controlli tipo TextBox venissero gestiti automaticamente.
Sorry for the ignorance...
Finalmente torno online :D
Prova a guardare questo http://jachman.wordpress.com/2006/07/11/synchronising-the-winform-data-binding-using-net-20/ .
alkl.