Perché il mio Entity Framework Code Prima raccolta proxy nullo e perché non posso impostarla?

Sto usando DBContext e ho due classi le cui proprietà sono tutte virtuali. Posso vedere nel debugger che sto ottenendo un object proxy quando interrogo il contesto. Tuttavia, una proprietà di raccolta è ancora nullo quando provo ad aggiungerla. Pensavo che il proxy avrebbe assicurato che la raccolta fosse inizializzata.

Poiché il mio object Poco può essere utilizzato al di fuori del suo contesto di dati, ho aggiunto un controllo per il fatto che la raccolta è nullo nel costruttore e la creo se necessario:

public class DanceStyle { public DanceStyle() { if (DanceEvents == null) { DanceEvents = new Collection(); } } ... public virtual ICollection DanceEvents { get; set; } } 

Funziona al di fuori del contesto dei dati, ma se recupero un object utilizzando una query, sebbene il test sia vero, quando provo ad impostarlo, ottengo la seguente eccezione: “La proprietà” DanceEvents “sul tipo” DanceStyle_B6089AE40D178593955F1328A70EAA3D8F0F01DDE9F9DBD615F60A34F9178B94 “non può essere impostata perché il la raccolta è già impostata su EntityCollection. ‘

Posso vedere che è nullo e non posso aggiungerlo, ma nemmeno posso impostarlo su una raccolta perché il proxy dice che è già impostato. Quindi non posso usarlo. Non ho capito bene.

Ecco la class DanceEvent:

 public class DanceEvent { public DanceEvent() { if (DanceStyles == null) { DanceStyles = new Collection(); } } ... public virtual ICollection DanceStyles { get; set; } } 

Ho omesso le altre proprietà del tipo di valore dal codice sopra. Non ho altri mapping per quelle classi nella class di contesto.

Come hai osservato correttamente nella risposta alla tua stessa domanda, rimuovere la parola chiave “virtuale” dalle proprietà della raccolta aggira il problema, impedendo a Entity Framework di creare un proxy di rilevamento delle modifiche. Tuttavia, questa non è una soluzione per molte persone, perché i proxy di monitoraggio delle modifiche possono essere davvero convenienti e possono aiutare a prevenire i problemi quando si dimentica di rilevare le modifiche nei punti giusti del codice.

Un approccio migliore sarebbe quello di modificare le tue classi POCO, in modo che esse istanzino le proprietà della raccolta nel loro access point, piuttosto che nel costruttore. Ecco la tua class POCO, modificata per consentire la creazione del proxy di rilevamento delle modifiche:

 public class DanceEvent { private ICollection _danceStyles; public virtual ICollection DanceStyles { get { return _danceStyles ?? (_danceStyles = new Collection()); } protected set { _danceStyles = value; } } } 

Nel codice precedente la proprietà della raccolta non è più automatica, ma ha un campo di supporto. È meglio se lasci il setter protetto, impedendo a qualsiasi codice (diverso dal proxy) di modificare successivamente queste proprietà. Noterai che il costruttore non era più necessario ed è stato rimosso.

Ho trovato la soluzione a questo problema qui: codice prima aggiunta alle collezioni? Come usare Code First con i repository?

Ho rimosso “virtuale” da tutte le proprietà eccetto collezioni e oggetti caricati pigri, cioè tutti i tipi nativi.

Ma ancora non capisco come si possa finire con la situazione in cui si dispone di una raccolta nulla che non è ansible utilizzare e non si ha modo di impostarla su una raccolta valida.

Ho anche trovato questa risposta da Rowan Miller su un forum MSDN

Ciao,

Se rendi tutte le tue proprietà virtuali allora EF genererà classi proxy in fase di esecuzione che derivano dalla tua class POCO, questi proxy consentono a EF di scoprire le modifiche in tempo reale piuttosto che dover acquisire i valori originali del tuo object e quindi cercare le modifiche quando si salva (questo è ovviamente vantaggioso per prestazioni e memoria, ma la differenza sarà trascurabile a meno che non si abbia un gran numero di quadro caricate in memoria). Questi sono conosciuti come “proxy di monitoraggio delle modifiche”, se si rendono virtuali le proprietà di navigazione, viene comunque generato un proxy ma è molto più semplice e include solo una logica per eseguire il caricamento lento quando si accede a una proprietà di navigazione.

Poiché il codice originale stava generando proxy di rilevamento delle modifiche, EF sostituiva la proprietà della raccolta con un tipo di raccolta speciale per aiutarlo a scoprire le modifiche. Poiché si tenta di impostare la raccolta su un semplice elenco nel costruttore, si ottiene l’eccezione.

A meno che tu non veda problemi di prestazioni, seguirò il suggerimento di Terrence e rimuoverò semplicemente “virtuale” dalle tue proprietà di non navigazione.

~ Rowan

Quindi sembra che ho solo il problema con un completo ‘proxy di tracciamento delle modifiche’ se tutte le mie proprietà sono virtuali. Ma dato ciò, perché non posso ancora usare la proprietà virtuale sul proxy di tracciamento delle modifiche? Questo codice esplora sulla riga tre poiché ds2.DanceEvents è nullo e non può essere impostato nel costruttore:

 DanceStyle ds2 = ctx.DanceStyles.Where(ds => ds.DanceStyleId == 1).Single(); DanceEvent evt = CreateDanceEvent(); ds2.DanceEvents.Add(evt); 

Sono ancora confuso, anche se il mio codice ora funziona a causa della correzione di cui sopra.

Vecchia domanda …

Poco class:

 public partial class MyPOCO { public MyPOCO() { this.MyPocoSub = new HashSet(); } //VIRTUAL public virtual ICollection MyPocoSub { get; set; } } 

e il codice proxy:

  public override ICollection MyPocoSubSets { get { ICollection myPocoSubSets = base.MyPocoSubSets; if (!this.ef_proxy_interceptorForMyPocoSubSets(this, myPocoSubSets)) { return base.MyPocoSubSets; } return myPocoSubSets; } set { if (value != this.RelationshipManager.GetRelatedEnd("WindowsFormsApplication.Models.MyPocoSubSet_MyPOCO", "MyPocoSubSet_MyPOCO_Source")) { // EXCEPTION throw new InvalidOperationException("The property 'MyPocoSubSets' on type 'MyPOCO_A78FCE6C6A890855C68B368B750864E3136B589F9023C7B1D90BF7C83FD291AC' cannot be set because the collection is already set to an EntityCollection."); } base.MyPocoSubSets = value; } } 

Come puoi vedere l’eccezione sollevata nella class proxy in ExtityFramework 5. Ciò significa che il comportamento esiste ancora.