Una delle funzionalità più interessanti dell'Entity Framework è la possibilità di differire il caricamento dei dati negli oggetti di tipo Entity: questa caratteristica, nota come Lazy Loading, consente di alleggerire notevolmente il carico sul Database in quanto le query vengono effettuate soltanto nel momento in cui il codice ha effettivamente bisogno di valorizzare la proprietà richiesta.
Per capire meglio come funziona, prendiamo ad esempio il seguente sistema di classi di tipo Entity:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public class Student { public Student() { } public int StudentId { get; set; } public string StudentName { get; set; } public virtual StudentAddress StudentAddress { get; set; } public virtual ICollection<Teacher> Teachers { get; set; } } public class StudentAddress { [Key, ForeignKey("Student")] public int StudentId { get; set; } public string Address1 { get; set; } public string Address2 { get; set; } public string City { get; set; } public int Zipcode { get; set; } public string State { get; set; } public string Country { get; set; } public virtual ICollection<StudentAddressDetail> StudentAddressDetails { get; set; } public virtual Student Student { get; set; } } |
Possiamo notare come la proprietà StudentAddress della classe Student sia un tipo complesso che fa riferimento a una Entity specifica (StudentAddress), a sua volta relativa a una tabella presente nel nostro Database. Richiamando una lista di studenti con la proprietà LazyLoading abilitata il nostro data provider caricherà tutti gli studenti dal DB, ma non si preoccuperà di valorizzare la proprietà StudentAddress fino a quando il nostro codice non la richiederà esplicitamente con una chiamata apposita.
1 2 3 4 5 6 7 8 9 |
using (var ctx = new SchoolDBEntities()) { // Loads only the students IList<Student> sList = ctx.Students.ToList<Student>(); Student s = sList[0]; // Loads this StudentAddress for this specific user only (with a separate SQL query) StudentAddress a = s.StudentAddress; } |
Disabilitare il Lazy Loading
In Entity Framework 4 e successive il Lazy Loading è abilitato per impostazione predefinita. E' possibile disabilitarlo sia globalmente, a livello di DbContext, che selettivamente, a livello di singola proprietà.
Per disabilitarlo a livello globale è sufficiente impostare la proprietà LazyLoadingEnabled dell'oggetto Configuration del DbContext in fase di inizializzazione dell'oggetto:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
using System; using System.Data.Entity; using System.Data.Entity.Infrastructure; using System.Data.Entity.Core.Objects; using System.Linq; public partial class SchoolDBEntities : DbContext { public SchoolDBEntities(): base("name=SchoolDBEntities") { this.Configuration.LazyLoadingEnabled = false; } } |
Per disabilitarlo a livello di singola proprietà è sufficiente rimuovere l'attributo virtual. Riprendendo l'esempio precedente, è sufficiente modificare la classe Student nel seguente modo:
1 2 3 4 5 6 7 8 9 10 11 |
public class Student { public Student() { } public int StudentId { get; set; } public string StudentName { get; set; } // non-virtual property - lazy load disabled public StudentAddress StudentAddress { get; set; } // non-virtual property - lazy load disabled public ICollection<Teacher> Teachers { get; set; } } |
IMPORTANTE: Non commettete l'errore di pensare che, una volta disabilitato il Lazy Loading, il framework valorizzerà automaticamente tutte le proprietà relative a oggetti correlati: al contrario, una volta che lo avrete disabilitato dovrete essere voi (e il vostro codice) a farvi carico in modo esplicito di tale operazione. Vediamo come.
Caricare manualmente i dati
Il caricamento manuale dei dati può essere effettuato in due modi: a livello di Entity Request (Eager Loading) oppure a livello di singola proprietà.
Property Loading
Il metodo più semplice è il cosiddetto Property Loading, ottenibile utilizzando il metodo Load() messo a disposizione dall' entity framework:
1 2 3 4 5 6 7 8 |
using (var ctx = new SchoolDBEntities()) { // Loads the student with Primary Key 1 Student s = ctx.Students.Find(1); // Load the student's related StudentAddress ctx.Entry(s).Reference(i => i.StudentAddress).Load(); } |
Il metodo può essere utilizzato anche nel caso di relazioni uno a molti. Ad esempio, nel caso in cui volessimo valorizzare anche la collection virtuale Teachers prevista per ciascun elemento Student potremmo procedere nel seguente modo:
1 2 3 4 5 6 7 8 |
using (var ctx = new SchoolDBEntities()) { // Loads the student with Primary Key 1 Student s = ctx.Students.Find(1); // Load the student's related StudentAddress ctx.Entry(s).Collection(i => i.Teachers).Load(); } |
Eager Loading
Il procedimento noto come Eager Loading consiste nel caricare sia le Entity che una o più Entity ad esse correlate. Per impostarlo è necessario utilizzare il metodo Include() nel seguente modo:
1 2 3 4 5 6 |
using (var ctx = new SchoolDBEntities()) { // Loads the students AND all related StudentAddress using Eager Loading IList<Student> sList = ctx.Students.Include(s => s.StudentAddress).ToList<Student>(); Student s = sList[0]; } |
IMPORTANTE: Se il compilatore non riconosce il metodo Include(), assicuratevi di aver aggiunto un riferimento al namespace System.Data.Entity al vostro codice.
E' anche possibile utilizzare l'Eager Loading per valorizzare relazioni su più livelli. Ad esempio, nel caso in cui volessimo valorizzare anche la collection virtuale StudentAddressDetails per ciascun elemento StudentAddress (cfr. definizione della classe a inizio articolo) potremmo procedere nel seguente modo:
1 2 3 4 5 6 |
using (var ctx = new SchoolDBEntities()) { // Loads the students AND all related StudentAddress AND all related StudentAddressDetails using Eager Loading IList<Student> sList = ctx.Students.Include(s => s.StudentAddress.StudentAddressDetails).ToList<Student>(); Student s = sList[0]; } |
E così via.
Per il momento è tutto: felice sviluppo!