Ciao a tutti,vi "rimbalzo" alcune questioni architetturali con cui mi stoscontrando in questi giorni.1. Il primo problema è l'impedence mismatch tra il dominio veroe proprio ed il modello dei dati. I casi potrebbero sembrarebanali ma data la scarsa preparazione/esperiensa delsottoscritto... ;-)Supponiamo che io abbia le seguenti entità:class Foo{ object PK; //PrimaryKey int Seed; string Label; Bar FooBar;}class Bar{ object PK; string Tag; bool IsOpen;} 1.a) In un'ottica di caricamento "lazy" ha senso che il Foo interroghi ilrepository dei Bar quando qualcuno accede alla sua propety FooBar?Questo però non significa inserire una dipendenza tra le entità el'infrastruttura applicativa? E' "stilisticamente" corretto?1.b) occorre che la PK di ogni istanza rimanga sempre la stessa. Comeposso fare affinché sia solo il Dal ad essere in grado di settarequesta property? Eventualmente potrebbe avere senso imporre un vincolosimile anche per l'entità aggregata Bar, ossìa imporre che sia solo ilDal a poter settare l'istanza di Bar in Foo e che poi questa istanzarimanga sempre la stessa? In sintesi: la possibilità di avere propertyche sono read-only al di fuori del Dal implica *necessariamente* unalayerizzazione? (Vi prego, ditemi di no...)1.c) Supponendo di aver risolto il problema precedente, mi trovoora a dover gestire una collezione di elementi in unaproperty; una cosa del tipo:class Foo{ object PK; ICollection<Widget> Widgets;}class Widget{ object PK; int X; int Y;}In uno scenario del genere la logica di riempimento della collectionWidget deve essere "cablata" nella collection stessa?2. Il secondo problema riguarda le connessioni al db.All'attuale stato delle cose il mio data mapper per ogni query chedeve eseguire apre una connessione, esegue la query, chiude laconnessione. Supponendo di dover fare diverse query per ognitransazione/operazione/quello-che-volete, non ho un overhead eccessivo?3. Il terzo problema la ricerca con query su entità che sono statemodificate. Supponiamo infatti che per i miei Foo le Label debbanoessere univoche e abbia tre oggetti Foo: x, con PK=1 e label="x"; y, con PK=2 e label="y"; z, con PK=3 e label="z";Inizia una transazione e cambio la label di x in "--x". Poi vogliocreare un nuovo Foo nuovo_x con label "x". Se vado a fare unGetBy(...) sul data context questo lo inoltra al data mapper di Fooche ritorna come "1" come PK della riga per cui label="x", il datacontext ritornerà dunque l'elemento x, ma la cui label non è "x". In sintesi: devo implementare anche il GetBy per le entità giàfetchate?Grazie di cuore e scusate se vi ammorbo con i miei dubbi sui massimisistemi. Buon finesettimana,Giulio--
Ciao Mauro,
innanzitutto grazie per la risposta e complimenti ancora per l'avatar dell'immenso Gene! ;-)
Mauro Servienti: tecnicamente si, stilisticamente no.. mettiamola cos� un ORM fa esattamente quello solo che in un proxy della tua entit� di dominio e quindi per te la scosa � trasparente, ma un ORM � decisamewnte evoluto e in base al mapping sa cosa proxare e come, se lo fai a mano utilizzare un proxy generico senza introdurre il concetto di mapping � pressoch� impossibile... esperienza proprio di questo periodo.
Uhm... fammici ragionare un po'.
In poche parole le entità che vengono "create" da un O/RM non sono entità vere e proprie ma dei proxy alle entità stesse.
A questo punto la domanda è d'obbligo: quando uso un O/RM (tipo EF o NH) è possibile "ispezionare" il codice che viene generato?
Mauro Servienti: usa un costruttore luke ;-) abstract class Bar { protected Bar( Object pk, Foo child ) { } } class BarProxy { internal BarProxy( Object pk, Foo child ) : base( ... ) { } }
usa un costruttore luke ;-) abstract class Bar { protected Bar( Object pk, Foo child ) { } } class BarProxy { internal BarProxy( Object pk, Foo child ) : base( ... ) { } }
Uhm... anche qui dovrei ragionarci un po' ma... non siamo punto a capo?
O forse no... va be', ci ragiono un po'...
Mauro Servienti: No, sempre o costruttore, e quindi eager-load, o lazy-load.
Ok... ma se la mia collection implementa una logica particolare (es: limite del numero di elementi e blablabla)?
Mauro Servienti: Il connection-pool del db � li apposta, potresti ipotizzare di passarti in giro la connessione tra i data-mapper, la crei nel DataContext e la passi al mondo sottostante.
Il connection-pool del db � li apposta, potresti ipotizzare di passarti in giro la connessione tra i data-mapper, la crei nel DataContext e la passi al mondo sottostante.
Il che è una ottima idea...
Mauro Servienti: > 3. Il terzo problema la ricerca con query su entità che sono state > modificate. Supponiamo infatti che per i miei Foo le Label debbano > essere univoche e abbia tre oggetti Foo: > x, con PK=1 e label="x"; > y, con PK=2 e label="y"; > z, con PK=3 e label="z"; > Inizia una transazione e cambio la label di x in "--x". Poi voglio > creare un nuovo Foo nuovo_x con label "x". Se vado a fare un > GetBy(...) sul data context questo lo inoltra al data mapper di Foo > che ritorna come "1" come PK della riga per cui label="x", il data > context ritornerà dunque l'elemento x, ma la cui label non è "x". > In sintesi: devo implementare anche il GetBy per le entità già > fetchate? Non mi � ben chiaro lo scenario ma direi IdentityMap... e qui son caz*i ;-)
> 3. Il terzo problema la ricerca con query su entità che sono state > modificate. Supponiamo infatti che per i miei Foo le Label debbano > essere univoche e abbia tre oggetti Foo: > x, con PK=1 e label="x"; > y, con PK=2 e label="y"; > z, con PK=3 e label="z"; > Inizia una transazione e cambio la label di x in "--x". Poi voglio > creare un nuovo Foo nuovo_x con label "x". Se vado a fare un > GetBy(...) sul data context questo lo inoltra al data mapper di Foo > che ritorna come "1" come PK della riga per cui label="x", il data > context ritornerà dunque l'elemento x, ma la cui label non è "x". > In sintesi: devo implementare anche il GetBy per le entità già > fetchate? Non mi � ben chiaro lo scenario ma direi IdentityMap... e qui son caz*i ;-)
Uhm... cerco di spiegarmi meglio, in termini più generali.
Io ho un insieme X di oggetti di tipo x che sono su un db, e una query Q(X) che seleziona un sottoinsieme di X (es: l'insieme di tutti gli impiegati con paga inferiore ai 40k euro l'anno). L'oggetto x1 viene modificato (gli viene dato un aumento di stipendio fino a 50k euro) ma la modifica non è persistente: quindi Q(X) conterrà x1 (perché sul db la paga di x1 è di 30k euro) mentre secondo lo stato "transiente" questo non dovrebbe esserci. L'unica cosa che mi viene in mente di fare è tradurre le query anche in criteri di ricerca sulle entità già nel contesto e poi confrontare le chavi con quelle dei risultati ritornati dal db e generare opportunamente il risultato. Che ne dici? Mi sono spiegato meglio? Ti prego dimmi di si... la mia padronanza della lingua italiana sta calando di giorno in giorno... ;-)
Ciao e grazie davvero,
Giulio
--
Mauro Servienti: > abstract class Bar > { > protected Bar( Object pk, Foo child ) > { > } > } > > class BarProxy > { > internal BarProxy( Object pk, Foo child ) >> base( ... ) > { > } > } la classe BarProxy � interna al tuo Dal quindi � solo li che puoi chiamare quel costruttore.
> abstract class Bar > { > protected Bar( Object pk, Foo child ) > { > } > } > > class BarProxy > { > internal BarProxy( Object pk, Foo child ) >> base( ... ) > { > } > } la classe BarProxy � interna al tuo Dal quindi � solo li che puoi
chiamare quel costruttore.
Dunque, ricapitolando: BarProxy è una classe che è sottoclasse di Bar, definita internamente al Dal che quindi può settare opportunamente i campi che per il resto del mondo sono read-only. Giusto?
Mauro Servienti: > Ok... ma se la mia collection implementa una logica particolare (es: limite > del numero di elementi e blablabla)? fammi un esempio concreto perch� non riesco a capire bene lo scenario.
> Ok... ma se la mia collection implementa una logica particolare (es: limite > del numero di elementi e blablabla)? fammi un esempio concreto perch� non riesco a capire bene lo scenario.
Uhm... faccio l'esempio più stupido del mondo, ok?
Ho una entità X che espone un enumeratore che può avere tre valori (0, 1, 2) più una serie di altre property. La mia entità Y ha come property un vettore di X in cui però ce ne possono essere UNA con enum che vale 0, UNA con enum che vale 1, UNA con enum che vale 2.
Mauro Servienti: tecnicamente � giusto cos�, se quello che vuoi � che *durante* la *stessa* *transazione* di *business* ti venga ritornato l'oggetto modificato allora la soluzione � IdentityMap... altrimenti mi sa che la tua padronanza delle lingua italiana fa acqua :-D using( IDataContext dc = dataContextFactory.Create() ) { var entity1 = dc.GetByKey( 12 ); entity1.Property = "new value"; var entity2 = dc.GetByKey( 12 ); Assert.AreEqual( entity1, entity2 ); Assert.AreEqual( entity1.Property, entity2.Property ); } ho capito bene?
tecnicamente � giusto cos�, se quello che vuoi � che *durante* la *stessa* *transazione* di *business* ti venga ritornato l'oggetto modificato allora la soluzione � IdentityMap... altrimenti mi sa che la tua padronanza delle lingua italiana fa acqua :-D using( IDataContext dc = dataContextFactory.Create() ) { var entity1 = dc.GetByKey( 12 ); entity1.Property = "new value"; var entity2 = dc.GetByKey( 12 ); Assert.AreEqual( entity1, entity2 ); Assert.AreEqual( entity1.Property, entity2.Property ); } ho capito bene?
Mi sa che ancora non ci siamo... ;-)
In riferimento al codice da te postato, supponiamo che le entity1 non possano avere duplicati nel valore di Property. La mia situazione è:
using( IDataContext dc = dataContextFactory.Create() ) {
dc.BeginTransaction()
var entity1 = dc.GetByKey( 12 ); entity1.Property = "pippo"; dc.Update(entity1)
//A QUESTO PUNTO
//cosa succede se faccio una query per cercare tutte le entity che hanno Property="pippo"?
}
Ciao e grazie,
Mauro Servienti:Ciao petrux, esatto, infatti se noti Bar � abstract, quindi l'unico modo per avere un'isanza di bar sar� andare da una factory
No, aspetta... qui le cose mi si stanno complicando e non poco.
Se io devo creare un nuovo Bar, come faccio? Puoi postarmi uno snnippet?
Mauro Servienti: > Ho una entità X che espone un enumeratore che può avere tre valori (0, 1, > 2) più una serie di altre property. La mia entità Y ha come property un > vettore di X in cui però ce ne possono essere UNA con enum che vale 0, UNA > con enum che vale 1, UNA con enum che vale 2. quindi lo strato DAL che far� o lazy o eager load sapr� cosa e come caricarlo.
Ok, ma se provo ad una collection "satura" provo ad aggiungere un altro x è la Add() della collection che deve sollevare eccezione?
Mauro Servienti: > using( IDataContext dc = dataContextFactory.Create() ) > { > > > dc.BeginTransaction() > > var entity1 = dc.GetByKey( 12 ); > entity1.Property = "pippo"; > dc.Update(entity1) > > //A QUESTO PUNTO > > //cosa succede se faccio una query per cercare tutte le entity che hanno > Property="pippo"? > > } > continuo a non capire... :-) cosa dovrebbere succedere nel tuo esempio? prova a scrivere uno pseudo test che fallisce perch� nell'esempio che fai dc ti ritornerebbe anche entity1 ma non capisco perch� non dovrebbe
> using( IDataContext dc = dataContextFactory.Create() ) > { > > > dc.BeginTransaction() > > var entity1 = dc.GetByKey( 12 ); > entity1.Property = "pippo"; > dc.Update(entity1) > > //A QUESTO PUNTO > > //cosa succede se faccio una query per cercare tutte le entity che hanno > Property="pippo"? > > } > continuo a non capire... :-) cosa dovrebbere succedere nel tuo esempio? prova a scrivere uno pseudo test che fallisce perch� nell'esempio che fai dc ti ritornerebbe anche entity1 ma non capisco perch� non dovrebbe
No, no... io intendevo proprio il comtrario!
Se alla riga della tabella associata ad entity1 la Property vale "pluto", quando vado a cercare quelle righe per cui la property vale "pippo", la riga di entity1 non viene ritornata. Quindi l'identity map non la troverà e non sarà nella collection. Quindi le mie query dovranno anche tradursi in una "query" sulle entità già in cache, giusto?
Ciao,
Mauro Servienti:Ciao petrux, se cerchi il valore vecchio non lo troverai mai, giustamente. Il disallineamento casomai c'� finch� non fai update sul db ma � giusto che ci sia.
Ecco: il problema sta qui. Sono nella condizione per cui devo evitare questo disallineamento come la peste.
Come posso fare?
Ciao Mauro
Mauro Servienti: cio� tu vuoi che finch� non viene fatto un update una ricerca successiva sullo stesso data context ti ritorni il valore vecchio e non quello nella IM? Mi scrivi 2 righe di pseudo-test per capire?
quello nella IM?
Mi scrivi 2 righe di pseudo-test per capire?
Eccolo qui:
//esempio;
IDataContext dc = DataContextFactory.Create();
Foo x = dc.GetById(12);
Assert.AreEqual("pippo", x.Label); //in sostanza la label di x vale "pippo"
dc.BeginTransaction();
x.Label = "pluto";
IList<Foo> fooList = dc.GetByLabel("pluto");
//in realtà questo GetByLabel() non esiste, ma sarebbe una
//generica query di select;
Assert.IsTrue(fooList.Contains(x));
Chiaro? ;-) A me serve che la GetByLabel matchi anche una entità che soddisfa la query nel suo stato "pending".