Ho 2 modelli, uno dei quali ha una collezione figlio dell'altro:
[Table("ParentTable")]
public class Parent
{
[Key, Column("Parent")]
public string Id { get; set; }
[Column("ParentName")]
public string Name { get; set; }
public virtual ICollection<Widget> Widgets { get; set; }
}
[Table("WidgetTable")]
public class Widget
{
public string Year { get; set; }
[Column("Parent")]
public string ParentId { get; set; }
public string Comments { get; set; }
[Key, Column("ID_Widget")]
public int Id { get; set; }
[ForeignKey("ParentId"), JsonIgnore]
public virtual Parent Parent { get; set; }
}
Questo codice funziona per> 99% dei widget:
var parent = _dbContext.Parents.FirstOrDefault(p => p.Id == parentId);
Di solito, parent.Widgets
è una raccolta con più di un elemento. In un paio di casi, tuttavia, parent.Widgets
è nullo (non è una raccolta senza elementi).
Ho usato Query Analyzer per tracciare sia la query per il genitore che la query per i widget appartenenti a quel genitore. Entrambi restituiscono esattamente le file che mi aspetto; tuttavia, il modello per uno o due ID padre restituisce un valore null per la raccolta Widget. Cosa potrebbe causare una raccolta lazy-loaded per essere nullo in alcuni casi ma non in altri?
Questa situazione si verifica comunemente quando una durata di dbContext viene lasciata aperta attraverso un Add, saveChanges e quindi il recupero.
Per esempio:
var context = new MyDbContext(); // holding Parents.
var testParent = new Parent{Id = "Parent1", Name = "Parent 1"};
context.Parents.Add(testParent);
A questo punto se dovessi fare:
var result = context.Parents.FirstOrDefault(x=> x.ParentId == "Parent1");
non avresti un genitore. La selezione viene dallo stato impegnato. Quindi ...
context.SaveChanges();
var result = context.Parents.FirstOrDefault(x=> x.ParentId == "Parent1");
Questo ti restituirà un riferimento al genitore che hai inserito dal momento che il contesto conosce questa entità e ha un riferimento all'oggetto che hai creato. Non va allo stato dei dati. Poiché la definizione di Widget è stata appena definita con una proprietà automatica get / set, la raccolta Widget in questo caso sarà #null.
se lo fai:
context.Dispose();
context = new MyDbContext();
var result = context.Parents.FirstOrDefault(x=> x.ParentId == "Parent1");
In questo caso il genitore non è conosciuto dal nuovo contesto, quindi passa allo stato dei dati. EF ti restituirà un elenco di proxy per il lazy che carica i Widget, che non ce ne sono, quindi recuperi una lista vuota, non #null.
Quando si affrontano le classi di raccolta in EF, è meglio evitare le proprietà automatiche o inizializzarle nel costruttore per evitare questo comportamento; in genere vorrai assegnare widget dopo aver creato un genitore. L'inizializzazione di un membro predefinito è migliore perché non si desidera incoraggiare mai l'utilizzo di un setter sulla proprietà della raccolta.
Per esempio:
private readonly List<Widget> _widgets = new List<Widget>();
public virtual ICollection<Widget> Widgets
{
get { return _widgets; }
protected set { throw new InvalidOperationException("Do not set the Widget collection. Use Clear() and Add()"); }
}
Evita di eseguire un'operazione di Set su una proprietà di raccolta poiché ciò rovinerebbe negli scenari di riferimento delle entità. Ad esempio, se volessi ordinare la raccolta di widget per anno e fare qualcosa del tipo:
parent.Widgets = parent.Widgets.OrderBy(x=> x.Year).ToList();
Sembra abbastanza innocente, ma quando il riferimento Widget era un proxy EF, l'hai appena spazzato via. EF ora non può eseguire il rilevamento delle modifiche nella raccolta.
Inizializza la tua raccolta e dovresti evitare sorprese con #null riferimenti alle collezioni. Inoltre guarderei la vita del tuo dbContext. È bene mantenere uno inizializzato per tutta la durata di una richiesta o di un'operazione particolare, ma evitare di mantenerli in vita più a lungo del necessario. Il tracciamento del cambiamento del contesto e di tali risorse di consumo e puoi trovare un comportamento strano apparentemente intermittente come questo quando attraversano le operazioni.