Comportamento di un O/RM all'aggiunta di entità cancellate

rated by 0 users
This post has 3 Replies | 2 Followers

Top 10 Partecipanti
Maschio
Post 269
Punteggio 4.927
petrux Posted: 06-19-2009 17.44

Ciao a tutti,

sto cercando di "entrare" dentro un O/RM per capire un po' di cose e cercando di riscrivere un mio data context mi sono sorti alcuni dubbi. Supponiamo che abbia un data context che espone i seguenti metodi:

interface IDataContext
{
    void Add<T>(T item); //aggiunge l'item
    void Delete<T>(T item); //rimuove l'item
    void BeginTransaction(); //inizia una transazione
    void Commit(); //esegue il commit
    void Rollback(); //esegue il rollback
    bool IsInTransaction { get; } //dice se siamo all'interno di una transazione o no
}

Il codice dei test sulla base dei quali sto sviluppando il data context è il seguente.
** Premessa
IDataContext dataContext = ...;
dataContext.BeginTransaction();
Foo foo = new Foo();
dataContext.Add(foo);
...blablabla...

A questo punto arrivano i dubbi!
** Numero 1:

dataContext.Delete(foo);
dataContext.Add(foo);

In questo caso sollevo un'eccezione perché l'entità "foo" è stata cancellata ma la richiesta di delezione è ancora "pending". E' giusto?

** Numero 2 (segue sempre la premessa)

dataContext.Delete(foo);
dataContext.Commit();
...blablabla...
dataContext.BeginTransaction();
foo.Id = ""; //questo perché il data context accetta in Add solo entità con Id vuoto;
dataContext.Add(foo);

In questo caso non so proprio cosa fare.  Nel senso: una volta che le cancellazioni vengono committate devo continuare a mappare l'oggetto all'infinito oppure posso pensare di abilitare la ri-addizione?

grazie e buon finesettimana,
Giulio
--
http://www.giuliopetrucci.it
http://www.myspace.com/fujikomonamourtheband

Top 10 Partecipanti
Maschio
Post 249
Punteggio 3.458

Tutto parte dal concetto di come l'ORM capisce se un oggetto è nuovo o se è mappato. NHibernate ad esempio contrlla semplicemente l'id dell'oggetto, se questo è eguale al valore unsaved-value (di default è tipo 0 per gli interi, guid.empty per i guid etc) significa che è un oggetto transiente, non lo conosce per questo lo va a inserire con una INSERT.

Se gli viene passato nel saveorupdate un oggetto con Id non nullo allora significa che è stato precedentemente salvato, qui il modo di procedere dipende da come si è impostata la concorrenza etc, ma in generale l'ORM controlla nella identity map se l'oggetto è già in memoria, in questo caso non fa nulla, in caso contrario lo aggiunge alla identity map. Quando verrà il momento di propagare tutte le modifiche al db allora si genereranno le UPDATE corrispondenti.

Nel caso delle entità cancellate semplicemente si fa la stessa operazione dell'update, ma le entità vengono marcate come  cancellate, per questa ragione al momento del flush invece di una UPDATE si genererà una o più DELETE.

Se un oggetto viene marcato per la cancellazinoe, e poi riaggiunto l'ORM può essere in varie situazioni

1) non è ancora stata lanciata la DELETE per l'oggetto, si lancia una eccezione perchè non si può riaggiungere una entità cancellata.
2) La DELETE è già stata effettuata, l'identy map è ancora attiva. L'ORM controlla nella identity map, vede che c'è un oggetto ch eè stato cancellato e lancia eccezione perchè non si può riaggiungere un oggetto che è stato cancellato
3) La DELETE è stata effettuata, l'identity map è stata scartata (ad esempio con NHivbernate aggiungi l'oggetto cancellato ad un altra sessione). L'ORM crede di dovere fare un update, ma quando fa UPDATE vede che non ha aggiornato nulla e ti da errore.

A questo punto ecco la situazione del tuo ultimo caso

petrux:
dataContext.Delete(foo);
dataContext.Commit();
...blablabla...
dataContext.BeginTransaction();
foo.Id = ""; //questo perché il data context accetta in Add solo entità con Id vuoto;
dataContext.Add(foo);

In questo caso cancelli un oggetto, fai un flush con la commit, poi cambi l'id dell'oggetto è lo risalvi. La considerazione che faccio è "l'utilizzatore degli oggetti non dovrebbe avere possibilità di fare un set sull'id, ovvero gli id li deve poter gestire solo l'ORM, altrimenti ci si fa male (come in questo caso).

Nell'esempio in questione il problema è che l'orm, dato che vede un id vuoto, cataloga l'oggetto come transiente, lo mette nella idmap con il nuovo id. Questo potrebbe non comportarti problemi, nel senso che hai semplicemente creato un oggetto nuovo, riutilizzandone uno che era già presente in memoria, oppure potresti lanciare eccezione perchè verifichi che lo stesso oggetto, inteso come entità nella memoria managed, era precedentemente assegnato ad un altro id, e per questo lanci eccezione.

Io preferirei questo secondo metodo, aggiungendo magari un HashSet per tenere traccia di tutte le entità che sono attualmente presenti nel DataContext.

Spero di averti per lo meno chiarito qualche cpsa ;)

Alk.

 

  • | Punteggio Post: 20
Top 10 Partecipanti
Maschio
Post 269
Punteggio 4.927

Ciao Gian Maria

 

Gian Maria Ricci:

Tutto parte dal concetto di come l'ORM capisce se un oggetto è nuovo o se è mappato.

[stramegacut]

Nell'esempio in questione il problema è che l'orm, dato che vede un id vuoto, cataloga l'oggetto come transiente, lo mette nella idmap con il nuovo id. Questo potrebbe non comportarti problemi, nel senso che hai semplicemente creato un oggetto nuovo, riutilizzandone uno che era già presente in memoria, oppure potresti lanciare eccezione perchè verifichi che lo stesso oggetto, inteso come entità nella memoria managed, era precedentemente assegnato ad un altro id, e per questo lanci eccezione.

Io preferirei questo secondo metodo, aggiungendo magari un HashSet per tenere traccia di tutte le entità che sono attualmente presenti nel DataContext.

 

...ed inspiegabilmente è proprio ciò che ho reputato giusto anche io.

Quindi le alternative sono due: o io sto imparando qualcosa o tu stai regredendo! ;-)

Scherzi a parte... grazie e buona giornata,

Giulio

--

 

Top 10 Partecipanti
Maschio
Post 249
Punteggio 3.458

La decisione di lanciare eccezione quando un oggetto che è in "pending Delete" viene riaggiungo è sicuramente necessaria, evita infatti di avere incongruenze. Il fatto di cambiare Id e riutilizzare l'oggetto può essere comunque lecito, dipende solamente da come l'ORM gestisce il concetto di identità. Dato che il chiamante dovrebbe usare a scatola chiusa un ORM, senza dovere sapere come internamente si gestisce le IdMap è buona norma evitare di cambiare gli id a mano.

Permettere all'utilizzatore di giocare con gli Id potrebbe ad esempio causare un problema nell'ORM, che magari potrebbe avere lo stesso oggetto nella idmap interna associato a due id differenti. Questo può o meno causare problemi (dipende da come l'ORM opera sulla sua idmap), ma solitamente si cerca di evitare di creare aliasing o sovrapposizioni, per cui lanciare l'eccezione è la cosa migliore.

Cambiare l'id di un oggetto che è mappato in un ORM, anche se poi non lo si riaggiunge con il nuovo id può causare problemi interni, questo perchè l'oggetto è associato nella idmap al vecchio id, ma ora ha un ID differente.

In sostanza chi crea un ORM mette sempre delle barriere che impediscono di fare queste cose.

petrux:
Quindi le alternative sono due: o io sto imparando qualcosa o tu stai regredendo! ;-)

:) Solitamente nelle discussioni tutti i partecipanti evolvono :D.

Ciaooooooo.

Alk.

 

  • | Punteggio Post: 5
Pagina 1 di 1 (4 elementi) | RSS
Powered by Community Server (Commercial Edition), by Telligent Systems