Ciao a tutti,
visto che oggi se ne è parlato tanto, butto giù un altro post sull'argomento... In pratica mi sto trovando a fronteggiare questo problema: seguendo l'esempio "costruisci il tuo DAL" nel capitolo 6 del libro di Andrea Saltarello, mi sono imbattuto nel problema dell'unicità degli oggetti.
Il testo fa giustamente notare che, se io chiedeo al mio DataContext un GetByKey(object key), il DataContext cerca l'oggetto nell'identity map e se non lo trova lo fa fetchare al DataMapper.
La questione ora è: cosa succede nel GetAll<T>() o nel GetByCriteria(Query)? Onestamente non saprei proprio come implementare la cosa...
grazie,
Giulio
Una soluzione è questa, prima fai la query al db per tornare tutti i dati che soddisfano la query, poi per ogni dato quando vai a cercare di reidratare l'entità, ovvero ricreare l'oggetto in memeoria, controlli nell'identity map se è già presente, se si usi quello altrimenti reidtrati l'entità e la metti nell'identity map.
nhibernate segue questo approccio, infatti prima delle query effettua un flush per essere sicuro che il db sia allineato.
Se nel tuo querymodel usi linq, potrebbe essere più interessante usare la query come linq to object sugli oggetti in memoria, poi eseguirla nel db, cosi non devi fare flush per evitare problemi con le entità dirty.
alk.
--Blog Eng: http://www.codewrecks.com/blogBlog Ita: http://blogs.ugidotnet.org/rgmTwitter: http://twitter.com/alkampfer
>Una soluzione è questa, prima fai la query al db per tornare tutti i dati che soddisfano la query,>poi per ogni dato quando vai a cercare di reidratare l'entità, ovvero ricreare l'oggetto in memeoria, >controlli nell'identity map se è già presente, se si usi quello altrimenti reidtrati l'entità e la metti nell'identity map.
>nhibernate segue questo approccio, infatti prima delle query effettua un flush per essere sicuro che il db sia allineato.
praticamente tutti gli O/RM (es: EF) fanno così, perchè il "costo" di produrre una query filtrata è considerato superiore al "risparmio" ottenuto: tipicamente, infatti, il "costo vero" non è costituito dalla esecuzione della query/fetch dei dati, bensì dalla materializzazione degli oggetti e, da questo punto di vista, le due strategie hanno un comportamento analogo
.A
'giorno Giulio, giusto una piccola nota: da un punto di vista concettuale, la risposta di Mauro non fa una piega. Sempre da un punto di vista concettuale, allora, il life cycle della identity map non dovrebbe coincidere con quello della unit of work, bensì con le sue transazioni. Ciò premesso, però, come già ebbi modo di affermare qui: http://blogs.ugidotnet.org/pape/archive/0001/01/01/identity-map-non-e-una-cache.aspx
L'implementazione di una identity map è proprio uno dei casi definibili: "vorrei ma non posso", nei quali implementare un comportamento concettualmente irreprensibile ha un costo (effort, performance, ...) tipicamente ingiustificato. Il buon Francesco Salvi negli anni '80 avrebbe cantato "Pragmatismo senza limitismo, esagerare!" :-)
Ah, dimenticavo... l'avatar con Gene Simmons è *impagabile* :-)
>Uhm... ogni bambino che so rispetti chiederebbe: "e perché"? :-)
Giusto per capire, così so "tarare" la risposta: hai letto il post che ho referenziato (o almeno la descrizione di identity map sul P of EAA) prima di fare questa domanda? :-)
>se qualcuno usa ancora >l'entità X (intesa come oggetto fisico) ha senso "svuotare" l'IM?
La risposta è "si": poichè non sei più all'interno di una transazione, è giusto che tu ottenga i dati "veri" in quel momento. O meglio... "Sarebbe", giacchè tipicamente (e soprattutto *comprensibilmente*) nessun DAL o O/RM lo fa :-)
Ciao, vediamo di dare qualche spiegazione.
Per reidratare si intende l'operazione di ricostruire l'oggetto in memoria dai dati tornati dal database. Tipicamente l'ORM esegue la query, ottiene un datareader e per ogni riga ricostruisce gli oggetti prendendo i dati dal db. E' in questo momento che un orm controlla la chiave primaria nel datareader, controlla nella IM se è presente un oggetto con quella chiave, se si prende quello e non reidtrata nulla.
Il flush consiste in questo, l'orm controlla tutti gli oggetti in stato persistente, verifica se qualcuno ne ha cambiato qualche proprietà (controllando nel suo contesto di persistenza), se gli oggetti sono cambiati in memoria vengono propagati i cambiamenti al db con istruzioni UPDATE. NHibernate fa un flush prima di ogni queri per evitare che
Carico l'0oggetto A, cambio una sua proprietà, diciamo nome da "Paolo" a "Gian Paolo". Poi faccio una query per tutte le entità il cui nome isizia con "Pao". In questo caso il record nel db che corrisponde all'oggetto A ancora ha il valore "paolo" ma in memoria è diventato "Gian Paolo". Se non si fa il flush, l'orm esegue la query, l'oggetto viene tornato nel datareader (perchè nel db ancora vale paolo), poi viene controllata l'identitymap, e non viene reidtratato l'oggetto, in questo modo ti viene tornata una entità il cui nome è "Gian Paolo" .... cosa che non è concettualmente corretta. Un flush prima delle query risolve il tutto.
Per entità dirty si intendono tutte le entità persistenti che hanno proprietà cambiate, e che quindi non corrispondono più al contenuto del db.
cmq ti consiglio anche "java persistence with hibernate" o NHibernate in action. Parlano di hibernate e NHibernat eripsettivamente, ma contengono molti concetti applicabili a qualsiasi ORM. Naturalmente prima va letto il fowler che contiene tutti i pattern di base .
ciaooooo.
Alk.