Come mi piace implementare il Lazy Load con un pizzico di polimorfismo...

[English Version]

Leggendo il post di Giancalo "NHibernate: Session e Lazy Loading" mi sono ricordato che è da tempo che volevo bloggare come recentemente sto implementando il Lazy Load... è bene premettere che sebbene il post inizia con riferimento ad un post su NHibernate questo post non riguarda tale tecnologia ma riguarda una possibile tecnica di lazy load.

Partiamo dal primo mio concetto... come vedo/mi piace vedere la divisione tra i layer di una applicazione? Ogni layer può parlare solo con quello direttamente sotto di lui, tutti i layer conoscono il modello (le entità di business) e ovviamente per esigente tecniche (evitare referenza circolare) le entità di business non conoscono nessuno.

Come implementare un lazy load? Tempo fa intrapresi la discussione con Emanuele Del Bono con un interessante scambio di mail ... al tempo avevo la stessa idea di oggi sull'architettura - quella sopra descritta - ma risolvevo la cosa troppo macchinosamente... :( Alcuni risolvono la questione usando eventi che notificano l'esigenza delle entità di caricare proprietà ondemand... soluzione interessante ma non mi piace che l'entità sappia cosa è giusto caricare in modalità lazy e cosa no. La modalità di caricamento dei dati in fondo è - a mio avviso - una questione unicamente del DAL che sa meglio di chiunque altro cosa è conveniente relativamente alla base dati da lui gestita. Alla fine sono giunto alla consclusione che il polimorfismo è quello che fa al caso mio! :-D

Ecco qui a codice maccaronico quello che intendo tralasciando la questione di DAL astratto e DAL concreti di cui ho già espesso parere in "Archietture a plugIn, pensieri e considerazioni "...

[Assembly del Modello]
public class Person
{
     private Guid code= Guid.Empty;

     public virtual Guid Code
     {
        get{return code;}
        set{code= value};
     }

     private string name = string.Empty;

     public virtual string Name
     {
        get{return name;}
        set{name = value};
     }
    
     private AddressCollection addresses = new AddressCollection ();

     public virtual AddressCollection Addresses
     {
        get{return addresses ;}
        set{addresses = value};
     }
}
[Assembly di un DAL concreto]
class PersonDataProvider: IPersonDataProvider
{
     
     public Person RetrievePersonByCode(Guid code)
     {
         //TODO: Preparo il command per estrarre dal database la Persona dato il codice.    
         
         IDataReader r = cmd.OpenReader();
         if(r.Read())
         {
           Person person = new InnerPerson();
           person.Code = (Guid)r["Code"];
           person.Name = (String)r["Name"];
           person.Addresses = null;
           return person;
         }else{
           return new UnknownPerson();
         }
     }
     
     class InnerPerson: Person
     {
         public override AddressCollection Addresses
         {
            get
            {
              if(base.Addresses == null)
              {
                //TODO: Preparo il comando per estrarre gli indirizzi per questa persona...
                //TODO: Creo e popolo la collezione degli indirizzi (base.Addresses).
              }
              retturn base.Addresses;
            }
            set{base.Addresses = value;}
         }
     }
}

Polimorfismo: "la possibilità che una classe derivata ridefinisca i metodi e le proprietà dei suoi antenati rende possibile che gli oggetti appartententi ad una classe che ha delle sottoclassi rispondano diversamente alle stesse istruzioni. I metodi che vengono ridefiniti in una sottoclasse sono detti "polimorfi", in quanto lo stesso metodo si comporta diversamente a seconda del tipo di oggetto su cui è invocato"

"You state that you want a function to have the flexibility of late-binding properties using the keyword virtual. You don’t need to understand the mechanics of virtual to use it, but without it you can’t do object-oriented programming in C++. In C++, you must remember to add the virtual keyword because, by default, member functions are not dynamically bound. Virtual functions allow you to express the differences in behavior of classes in the same family. Those differences are what cause polymorphic behavior." (Bruce Eckel, Thinking in C++)

Technorati Tags:

posted @ giovedì 5 gennaio 2006 17.34

Print

Comments on this entry:

# re: Come mi piace implementare il Lazy Load con un pizzico di polimorfismo...

Left by Emanuele DelBono at 05/01/2006 18.34
Gravatar
Soluzione interessante.
Però in questo modo InnerPerson ha un reference al DAL per caricarsi i dati...o sbaglio?
Ne parliamo via mail ;-) o sul forum.
ciao.
.ema

# re: Come mi piace implementare il Lazy Load con un pizzico di polimorfismo...

Left by M.rkino at 05/01/2006 21.29
Gravatar
Beh InnerPerson ha riferimento al DAL... ma InnerPerson è parte del DAL. Il modello è Person, il dominio applicativo (BLL) ha i il concetto di Person e oltre tutto non può nemmeno provare a fare upcasting... InnerPerson è un nested class non pubblica ;-p

# re: Come mi piace implementare il Lazy Load con un pizzico di polimorfismo...

Left by Luca Mauri at 05/01/2006 21.37
Gravatar
Interessante...anche io ho la stessa architettura di entity/dal/bol e mi era capitato di confrontarmi con un mio collega che invece ha implementato una soluzione diversa, diciamo alla "orm", dove le entità contengono i metodi bol.
Con la soluzione del mio collega è ovvio che è semplice implementare il lazy loading.
Con la mia( e la tua) il tutto si complica e l'unica soluzione(non implementata) era appunto quella della notifica tramite eventi.

Ma ora vedo la tua soluzione che sembra veramente ottima.
Rimane il problema, come dice Emanuele, del riferimento al DAL presente nella InnerPerson.

A meno che non si consideri la InnerPerson una classe di appoggio, interna al namespace del DAL, dal momento che nel BOL farò sempre riferimento alle entità pure.

# re: Come mi piace implementare il Lazy Load con un pizzico di polimorfismo...

Left by M.rkino at 05/01/2006 22.09
Gravatar
Luca, grazie per l'apprezzamento alla soluzione. Cmq come ho detto nel commento al feedback di Emanuele... InnerPerson NON è da considerarsi parte del model ma parte del DAL (è una classe internal all'assembly del DAL concreto)... è - come l'hai definita - una _classe supporto_...

# re: Come mi piace implementare il Lazy Load con un pizzico di polimorfismo...

Left by Mauro Servienti at 05/01/2006 23.58
Gravatar
Ciao,

mi piace molto questa soluzione, ho fatto un po' di prove applicando questa metodologia al framework che mi sono costruito e rimuovendo quindi i meccanismi di LazyLoading via eventi e funziona che è una meraglia e soprattutto rimuove quelle brutture di eventi totalmente "sconnessi" dal domain model.
Una considerazione perchè non far diventare Person una classe astratta?, io l'ho implemetato così e non ho avuto problemi.

Complimenti!

.m

# re: Come mi piace implementare il Lazy Load con un pizzico di polimorfismo...

Left by M.rkino at 06/01/2006 0.09
Gravatar
beh Person non è astratta perchè non necessariamente deve essere ridefinita... diciamo che è bene che i modelli siano ridefinibili per favorire il polimorfismo. Se il DAL ritiene opportuno può usare il lazyload else usa l'entità as is... cmq Grazie :-D

# re: Come mi piace implementare il Lazy Load con un pizzico di polimorfismo...

Left by Riccardo Golia at 06/01/2006 2.26
Gravatar
Quando usi le collezioni come propriet, definiscile read-only, col solo getter implementato. Niente setter!

Durante l'inizializzazione della classe istanzi internamente la collezione vuota tramite un new. In seguito per caricare e rimuovere i dati, puoi sempre usare i metodi tra cui, Clear() e AddRange() della collezione. Se vuoi svuotare del tutto la collezione puoi usare Clear(), se vuoi fare una assegnazione (equivalente al setter) puoi usare AddRange().

Questo ti evita di fare una assegnazione alla propriet uguale a null (e ti evita possibili bachi). In ogni caso la propriet non sar mai uguale a null.

Per il lazy load puoi allora utilizzare la propriet Count della collezione e verificare che il numero di elementi sia o meno uguale a zero. Se specifichi un flag di caricamento, puoi distinguere il caso in cui nessun elemento sia presente nella collezione dalla condizione di pre-caricamento.

Ciao, Ricky.

# re: Come mi piace implementare il Lazy Load con un pizzico di polimorfismo...

Left by theEvil at 06/01/2006 2.42
Gravatar
Bella soluzione,

solo una curiosità...a questo punto il DAL non instanzierà mai un tipo Person ma sempre e solo InnerPerson, altrimenti ovviamente la collezione sarà sempre vuota...giusto ?
Quindi uso l'ereditarietà e il polimorfismo allo scopo di ottenere il late bound del polimorfismo stesso...che mi genera appunto il lazy loading : ho capito bene ?

Mi sfugge l'utilità di dove far implementare a PersonDataProvider l'interfaccia IPersonDataProvider...come mai questo ?

Infine se stai defininendo un provider concreto,quindi per uno specifico DB perchè utilizzare IDataReader e non per esempio SqlDataReader o OracleDataReader etc... ?

Ci sono alcuni punti che mi sfuggono...anche se penso di aver afferrato il senso generale della tua soluzione.
Complimenti anche da parte mia.

# re: Come mi piace implementare il Lazy Load con un pizzico di polimorfismo...

Left by M.rkino at 06/01/2006 10.20
Gravatar
ciao TheEvil, "Mi sfugge l'utilità di dove far implementare a PersonDataProvider l'interfaccia IPersonDataProvider...come mai questo?"
A tal proposito rimando al mio post sull'architettura plugin che ho referenziato nel post.
"Perchè utilizzare IDataReader"
Nel post ho semplicemente messo del Codice _maccaronico_... per far capire qllo che intendeco ;-p
Grazie dei complimenti! :-D

# re: Come mi piace implementare il Lazy Load con un pizzico di polimorfismo...

Left by CarmineM at 05/12/2006 21.30
Gravatar
Ciao M.rkino,
Mi stavo arrovellando proprio in questi giorni con l'implementazione del "Lazy Loading".
Leggendo da PeAA di Fowler e spulciando qualche esempio trovato in giro, ho optato per (nel caso delle collection) per un Proxy siffatto:
CollectionProxy<T> : ICollection<T>
...
public virtual Load() {
...
}

Le entità che contengono una collection da
popolare per mezzo del lazy loading, particolarizzano CollectionProxy<T> ridefinendo il metodo virtual "Load".

Es:
class RigheOrdineCollectionProxy : CollectionProxy<RigheOrdine>
...

public new void Load() {
...
Items = RigheOrdineDataMapper.GetByOrdineID(this.Parent.Id);
...
}

Poi, ho letto il tuo post che mi sembra
fornire una soluzione più immediata
di quella da me attualmente (parzialmente)
implementata.

Grazie, è un ottimo spunto.

# Quelli che il load lo vogliono lazy ma anche un p

Left by Web Log di M.rkino at 28/12/2006 13.29
Gravatar
A volte mi rendo conto che non solo ci farebbe piacere applicare il lazy load ma ci farebbe anche piacere che ci

# re: Come mi piace implementare il Lazy Load con un pizzico di polimorfismo...

Left by Adrian Florea at 13/02/2007 17.26
Gravatar
bellissima questa idea, M.rkino, complimenti!

domani la presento al corso che sto tenendo in questi giorni ;)

# re: Come mi piace implementare il Lazy Load con un pizzico di polimorfismo...

Left by M.rkino at 18/02/2007 21.57
Gravatar
Grazie mille Adrian! :-D

# Duz' Snapshot (2/3)

Left by TheDuzBlog! ;-p at 23/02/2007 12.08
Gravatar

# Duz' Snapshot (2/3)

Left by TheDuzBlog! ;-p at 23/02/2007 12.10
Gravatar
Proseguo con la corposa istantanea passando a NHibernate (

# Lazy loading in una riga

Left by Web Log di Adrian Florea at 29/05/2008 12.04
Gravatar
Lazy loading in una riga

# re: Come mi piace implementare il Lazy Load con un pizzico di polimorfismo...

Left by Franco at 19/10/2009 13.02
Gravatar
C'è una cosa che mi fa storcere il naso... è il client a dover scegliere se usare una classe o il suo proxy, a seconda dello use case. E' giusto, ma... anche se la classe proxy è nel package del DAL, è come se fosse nel model in quanto il client la vede. Sbaglio ?

# re: Come mi piace implementare il Lazy Load con un pizzico di polimorfismo...

Left by markino at 19/10/2009 14.03
Gravatar
mmm... non mi è molto chiaro il tuo dubbio, ma in genere il client non ha potere decisianale sulla strategia ma una Facade maschera la scelta decisa per il sistema.

La strategia è utilizzata molto bene - ad esempio da NH - che crea e carica dinamicamente i proxy iniettando la strategia scelta nel file di mapping. Un buon supporto di questo tipo di strategia è DynamicProxy di CastleProject. Questo si tratta di un vecchio post.

In ogni caso puoi vedere alcuni post correlati alle architetture dove applicare la soluzione dove il DAL è "wrappato" da facede.

Strategie di caricamento dei provider
blogs.ugidotnet.org/.../45884.aspx

L'evoluzione nel disegno di architetture ...
blogs.ugidotnet.org/.../45820.aspx

# re: Come mi piace implementare il Lazy Load con un pizzico di polimorfismo...

Left by Franco at 19/10/2009 15.30
Gravatar
In effetti, rileggendo la mia domanda, si vede che l'ho postata prima di pranzare !! Ho riletto meglio tutto ho solo bisogno di qualche chiarimento.

Sono assolutamente daccordo sull'uso di un facade. E' quello che faccio regolarmente.

Torniamo all'esempio e vediamo se ho capito. Il client, attraverso il facade, richiede sempre un oggetto della classe Person.
Il DAL (PersonDataProvider) ritorna sempre un oggetto di tipo InnerPerson, con il lazy load (addresses non è caricato inizialmente).
Veniamo alla domanda.
Se ci sono use case che non richiedono il lazy load sulla classe person, allora vorrei evitare di fare 2 accessi al db (uno per la parte non lazy e il resto quando serve) e quindi vorrei restituire un oggetto Person completamente popolato fin dalla prima richiesta.
La scelta di "quale Person" usare competerebbe al DAL e dipende dallo use case.
Però, pensandoci, potrebbe anche competere al BLL che potrebbe richiedere una versione di person con lazy load oppure completa (a seconda dello use case). Quindi il BLL potrebbe "iniettare" il comportamento richiesto al DAL... che ne pensi ?

# re: Come mi piace implementare il Lazy Load con un pizzico di polimorfismo...

Left by markino at 20/10/2009 11.22
Gravatar
Ciao Franco. La questione dello use case è assolutamente pertinente... e la scelta assoluta della strategia da associare ad uno specifica entità va sempre stretta. Se usi una mappatura "statica" tipica di un ORM l'unico modo per cambiare strategia è quello di avere dto diversi per casi d'uso diversi... anche se poi si tratta di dto che insistono sulla stessa entità concettuale. Questa è la via su cui personalmente mi sto muovendo. In alternativa usando l'esempio specifico del mio post ricorda che puoi avere più "InnerPerson" quante le strategie che vuoi avere relativamente ai diversi casi d'uso (InnerPersonCasoA, InnerPersonCasoB)... molto effort e manutenzione ma dovrebbe risponde alla tua domanda.

Your comment:



 (will not be displayed)


 
 
 
Please add 1 and 2 and type the answer here:
 

Live Comment Preview:

 
«marzo»
domlunmarmergiovensab
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910