Non so che questa è una cosa buona o cattiva e come adattarla.
Parent-Child ha una relazione molto semplice e ovvia. Nessun grosso problema. Puoi vedere il codice qui sotto:
public class Parent
{
public int Id {get;set;}
public List<Child> Childs {get;set;}
}
public class Child
{
public int Id {get;set;}
public int ParentId {get;set;}
public Parent Parent {get;set;}
}
var parent = parentRepository.FindAll(x=> x.Id == 10).ToList();
var childCollection = childRepository.FindAll(x=> x.ParentId == parent.Id).ToList();
Quando childCollection
il codice sopra, mi aspetto che un oggetto parent
con proprietà Childs
sia null
e childCollection
che abbia proprietà Parent
nulli. Ma questo non sta succedendo. Dopo la seconda riga di codice eseguita, parent.Childs
riempie con gli oggetti figlio e ogni proprietà Parent
in un oggetto figlio è uguale a parent
che non voglio (dovrei volerlo?).
Perché il lavoro su entità si comporta così? E in quali casi dovrei essere consapevole? Cosa succede se cambio childCollection
senza conoscere questo comportamento?
Sì, questo è il comportamento normale di EF. Nel momento in cui childCollection
viene materializzato chiamando ToList
, quando si verifica di nuovo l'istanza parent
, verrà eseguito il childCollection
relazioni e si controllerà se le entità Child
cui FK ha lo stesso valore del PK del parent
corrente sono state caricate precedentemente nel contesto dell'oggetto. In questo caso, la proprietà Parent.Childs
verrà immediatamente impostata con quelle entità Child
. Questo non ha nulla a che fare con il caricamento lazy, infatti il tuo modello non soddisfa tutti i requisiti di cui necessita il caricamento lento, come le proprietà di navigazione dovrebbero essere virtual
.
Questo comportamento non può essere disabilitato ma se si utilizza il metodo di estensione AsNoTracking al momento per creare le query, le entità restituite non verranno memorizzate nella cache in DbContext
:
var query= context.Childs.AsNoTracking().Where(c=>c.ParentId==10);
Puoi anche trovare maggiori dettagli in questo eccellente post
Come molti moderni ORM, Entity Framework è progettato non solo per associare i risultati delle query alle entità (oggetti), ma per mappare le loro relazioni alle proprietà. Se dichiari una proprietà di navigazione (come hai fatto con le proprietà Childs
o Parent
nelle tue classi), EF (pigramente) mapperà automaticamente tali proprietà alle loro entità referenziate (puoi trovare ulteriori informazioni sulle proprietà di navigazione qui ).
Questo comportamento è di progettazione e ha l'intento di evitare esplicitamente le query utilizzando join e rende superfluo chiamare childRepository.FindAll(x=> ...
perché tutte le entità Child
correlate vengono caricate da EF non appena si accede alla proprietà Childs
nel tuo oggetto Parent
.
Ricorda inoltre che EF implementa un sistema di tracciamento delle modifiche, quindi se modifichi le entità recuperate dal database, EF terrà traccia di tali modifiche e le persisterà la prima volta che SaveChanges
nel tuo contesto.
Come nota a margine, l'architettura EF è davvero un esempio dei pattern UnitOfWork e Repository usati insieme (DbContext è un UoW e ogni DbSet funziona come un repository), quindi consiglio vivamente di leggere di più su EF per evitare di usare impropriamente le sue funzionalità o -realizzandoli da soli usando un'altra astrazione (es. i tuoi Archivi).