many hasforeignkey framework foreign first dataannotations data code entity-framework collections poco proxies

entity framework - hasforeignkey - ¿Por qué mi Entidad Framework Code First First proxy null y por qué no puedo configurarlo?



entity framework relationships (3)

Como observó correctamente en la respuesta a su propia pregunta, eliminar la palabra clave "virtual" de las propiedades de la colección evita el problema al impedir que Entity Framework cree un proxy de seguimiento de cambios. Sin embargo, esta no es una solución para muchas personas, ya que los proxies de seguimiento de cambios pueden ser muy prácticos y pueden ayudar a prevenir problemas cuando se olvida detectar cambios en los lugares correctos de su código.

Un mejor enfoque sería modificar sus clases de POCO, para que creen una instancia de las propiedades de la colección en su acceso de obtención, en lugar de hacerlo en el constructor. Aquí está su clase de POCO, modificada para permitir la creación del proxy de seguimiento de cambios:

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

En el código anterior, la propiedad de la colección ya no es automática, sino que tiene un campo de respaldo. Es mejor si deja el setter protegido, evitando que cualquier código (que no sea el proxy) modifique posteriormente estas propiedades. Notará que el constructor ya no era necesario y se eliminó.

Estoy usando DBContext y tengo dos clases cuyas propiedades son todas virtuales. Puedo ver en el depurador que obtengo un objeto proxy cuando consulto el contexto. Sin embargo, una propiedad de colección sigue siendo nula cuando intento agregarla. Pensé que el proxy garantizaría que la recopilación se inicializara.

Debido a que mi objeto Poco se puede usar fuera de su contexto de datos, agregué un cheque para que la colección sea nula en el constructor y la creo si es necesario:

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

Eso funciona fuera del contexto de datos, pero si recupero un objeto usando una consulta, aunque la prueba es verdadera, cuando intento establecerlo, obtengo la siguiente excepción: ''La propiedad'' DanceEvents ''en el tipo'' DanceStyle_B6089AE40D178593955F1328A70EAA3D8F0F01DDE9F9FBD615F60A34F9178B94 ''no se puede configurar porque la la colección ya está configurada en una EntityCollection ''.

Puedo ver que es nulo y no puedo agregarlo, pero tampoco puedo establecerlo en una colección porque el proxy dice que ya está configurado. Por lo tanto, no puedo usarlo. Estoy confundido.

Aquí está la clase DanceEvent:

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

He omitido las otras propiedades de tipo de valor del código anterior. No tengo otras asignaciones para esas clases en la clase de contexto.


Encontré la solución a este problema aquí: ¿ Código primero que se agrega a las colecciones? ¿Cómo usar Code First con repositorios?

Eliminé ''virtual'' de todas las propiedades, excepto las colecciones y los objetos cargados, es decir, todos los tipos nativos.

Pero todavía no entiendo cómo puede terminar con la situación en la que tiene una colección nula que no puede usar y no tiene forma de establecerla en una colección válida.

También encontré esta respuesta de Rowan Miller en un foro de MSDN

Hola,

Si convierte todas sus propiedades en virtuales, EF generará clases de proxy en tiempo de ejecución que derivan de su POCO clasificado, estos proxies le permiten a EF descubrir los cambios en tiempo real en lugar de tener que capturar los valores originales de su objeto y luego buscar cambios. cuando guardas (esto obviamente tiene beneficios de rendimiento y uso de la memoria, pero la diferencia será insignificante a menos que tengas una gran cantidad de entidades cargadas en la memoria). Estos son conocidos como ''proxies de seguimiento de cambios'', si usted hace que sus propiedades de navegación sean virtuales, entonces aún se genera un proxy, pero es mucho más simple e incluye algo de lógica para realizar una carga diferida cuando accede a una propiedad de navegación.

Debido a que su código original generaba proxies de seguimiento de cambios, EF estaba reemplazando la propiedad de su colección con un tipo de colección especial para ayudarlo a descubrir los cambios. Como intenta establecer la colección en una lista simple en el constructor, obtiene la excepción.

A menos que vea problemas de rendimiento, seguiré la sugerencia de Terrence y simplemente eliminaré ''virtual'' de sus propiedades que no son de navegación.

~ Rowan

Entonces parece que solo tengo el problema con un ''proxy de seguimiento de cambios'' completo si todas mis propiedades son virtuales. Pero dado eso, ¿por qué aún no puedo usar la propiedad virtual en el proxy de seguimiento de cambios? Este código explota en la línea tres porque ds2.DanceEvents es nulo y no se puede establecer en el constructor:

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

Todavía estoy confundido, a pesar de que mi código está funcionando debido a la solución anterior.


Vieja pregunta ...

Clase de Poco:

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

y código proxy:

public override ICollection<MyPocoSubSet> MyPocoSubSets { get { ICollection<MyPocoSubSet> 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; } }

Como puede ver, esa excepción se presentó en la clase proxy en ExtityFramework 5. Esto significa que el comportamiento aún existe.