2009-02-07 14 views
14

Ho visto numerosi esempi di caricamento lazy - qual è la vostra scelta?Caricamento lento: qual è l'approccio migliore?

Data una classe del modello, ad esempio:

public class Person 
{ 
    private IList<Child> _children; 
    public IList<Child> Children 
    { 
     get { 
      if (_children == null) 
       LoadChildren(); 
      return _children; 
     } 
    } 
} 

La classe Person non deve sapere nulla di come è bambini vengono caricati .... o dovrebbe? Sicuramente dovrebbe controllare quando le proprietà sono popolate, o no?

Avresti un repository che accoppia una persona con la sua collezione di bambini o utilizzeresti un approccio diverso, ad esempio usando una classe lazyload - anche allora, non voglio che una classe lazyload si offuschi nella mia architettura del modello.

Come gestireste le prestazioni se prima richiedete una persona e poi i suoi figli (cioè non il caricamento pigro in questa istanza) o il caricamento in qualche modo pigro.

Tutto questo si riduce a una scelta personale?

+0

Problema: questo è abbastanza spesso un ottimo esempio di ottimizzazione locale a spese dell'ottimizzazione globale. – dkretz

risposta

14

Il miglior carico pigro è evitarlo;) La sicurezza del filo è un problema immediato da gestire. Non ho il conto di quanto spesso ho visto sistemi di produzione con 8 core CPU eseguire il caricamento lazy 8 volte per ogni singolo modello di caricamento lazy in uso. Almeno all'avvio del server, tutti i core del server hanno la tendenza a finire nelle stesse posizioni.

Lascia che un framework DI lo costruisca per te, se possibile. E se non puoi, preferisco ancora la costruzione esplicita. Quindi tutti i tipi di magia AOP semplicemente non li tagliano, vanno per la costruzione esplicita al di fuori della classe. Non metterlo nella classe persona, basta creare un servizio che costruisca gli oggetti nel modo corretto.

Presentazione di livelli "magici" che più o meno in modo trasparente fanno queste cose sembrano come una bella idea, ma devo ancora imbattersi in implementazioni che non hanno conseguenze impreviste e problematiche.

+0

Grazie per il tuo tempo, puoi approfondire l'utilizzo di un framework DI e di una costruzione esplicita? –

+0

Penso che stava dicendo che avere la classe Person in modo esplicito per creare i figli (con LoadChildren() presumibilmente) durante la sua inizializzazione non lascia dubbi sullo stato di _children. E Iniezione delle dipendenze, beh ci sono molti argomenti qui che sono meglio di me in un commento. – JMD

+0

Inoltre, si perde l'atomicità della query. E il più delle volte la strategia globale più efficiente è quella di emettere una singola query con join (esterni?) Per i record figlio e ottenere tutto in un'unica transazione, piuttosto che diffonderlo su più sub-hit del database. – dkretz

0

Sto pensando che questo è precisamente il tipo di problema che è meglio gestito da AOP (ad esempio PostSharp). Avere il tuo pigro carico come un aspetto e quindi usarlo per decorare pigramente qualsiasi proprietà che si desidera caricare. Disclaimer: non l'ho provato; solo pensando che è dovrebbe lavoro.

1

ho parlato di una soluzione che uso per realizzare lazy loading here

+0

Grazie per il tuo tempo, non è comunque solo un'altra dipendenza? Sono interessato all'approccio però. –

1

È possibile utilizzare il modello Virtual Proxy, insieme allo Observer pattern. Questo ti darebbe il carico pigro senza che la classe Person avesse una conoscenza esplicita di come vengono caricati i bambini.

0

Ho appena chiesto una domanda correlata here, ma era più pesante sull'immutabilità & virata di sicurezza del filo. Molte buone risposte e commenti. Potresti trovarlo utile

0

Ecco un esempio di attuazione lazy loading utilizzando il modello Proxy

La classe Person che vivere con il resto dei tuoi modelli. I bambini sono contrassegnati come virtuali in modo da poter essere sovrascritti all'interno della classe PersonProxy.

public class Person { 
    public int Id; 
    public virtual IList<Child> Children { get; set; } 
} 

La classe PersonRepository che vivrebbe con il resto dei repository. Ho incluso il metodo per ottenere i bambini in questa classe, ma potresti averlo in una classe ChildRepository se vuoi.

public class PersonRepository { 
    public Person FindById(int id) { 
     // Notice we are creating PersonProxy and not Person 
     Person person = new PersonProxy(); 

     // Set person properties based on data from the database 

     return person; 
    } 

    public IList<Child> GetChildrenForPerson(int personId) { 
     // Return your list of children from the database 
    } 
} 

La classe PersonProxy che convive con i repository. Questo eredita da Person e farà il caricamento pigro. Puoi anche usare un booleano per verificare se è già stato caricato invece di verificare se Children == null.

public class PersonProxy : Person { 
    private PersonRepository _personRepository = new PersonRepository(); 

    public override IList<Child> Children { 
     get { 
      if (base.Children == null) 
       base.Children = _personRepository.GetChildrenForPerson(this.Id); 

      return base.Children; 
     } 
     set { base.Children = value; } 
    } 
} 

Si può usare in questo modo

Person person = new PersonRepository().FindById(1); 
Console.WriteLine(person.Children.Count); 

Naturalmente si potrebbe avere PersonProxy prendere in un'interfaccia alla PersonRepository e accedervi tutto attraverso un servizio se non si desidera chiamare il PersonRepository direttamente.