2010-11-01 12 views
39

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.¿Por qué mi Entity Framework Code First First es nula y por qué no puedo configurarlo?

Debido a que mi objeto Poco se puede utilizar fuera de su contexto de datos, que añade un cheque por la colección siendo nula en el constructor y crearlo si es necesario:

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

que 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 establecer porque la colección ya está configurada en una EntityCollection. '

Veo 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í es 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 valor del código de seguridad. No tengo otras asignaciones para esas clases en la clase de contexto.

Respuesta

13

he encontrado la solución a este problema aquí: Code First adding to collections? How to use Code First with repositories?

me retiran 'virtual' de todas las propiedades excepto colecciones y objetos cargados perezosos, 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é this answer from Rowan Miller en un foro de MSDN

Hola,

Si realiza todas sus propiedades virtuales a continuación, EF generará clases de proxy en tiempo de ejecución que se deriva de su clasificado POCO, estos servidores proxy permiten EF para descubrir los cambios en tiempo real en lugar de tener que capturar los valores originales de su objeto y luego buscar cambios cuando ahorre (esto obviamente tiene beneficios de rendimiento y uso de la memoria, pero la diferencia será insignificante a menos que tenga 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.

Como su código original generaba proxies de seguimiento de cambios, EF estaba reemplazando su propiedad de 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

por lo que parece que sólo 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 hace saltar en la línea tres, porque ds2.DanceEvents es nula 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á trabajando ahora debido a la revisión anterior.

47

Como observó correctamente en la respuesta a su propia pregunta, eliminar la palabra clave "virtual" de las propiedades de 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, de modo que crean 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 POCO, modificado para permitir la creación de seguimiento de cambios de proxy:

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 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ó.

+0

Esa es otra forma de hacerlo pero no explica mi comentario: "Eso funciona fuera del contexto de datos, pero si recupero un objeto usando una consulta, aunque la prueba es verdadera, cuando intento establecerla, Obtiene la siguiente excepción: 'La propiedad' DanceEvents 'en el tipo' DanceStyle_B6089AE40D178593955F1328A70EAA3D8F0F01DDE9F9FBD615F60A34F9178B94 'no se puede establecer porque la colección ya está configurada en una EntityCollection.' Puedo ver que es nulo y no puedo agregar nada, pero tampoco puedo configurarlo en una colección porque el proxy dice que ya está configurado. Por lo tanto, no puedo usarlo. Estoy confundido ". –

+0

No puedo reproducir lo que describes. En mi experiencia, cuando la entidad se instancia como un proxy (ya sea como resultado de que sea devuelto por una consulta, o si se usa el método DbSet.Create), sus propiedades de recopilación se instancian con objetos EntityCollection. Nunca debe tener que establecer estas propiedades, simplemente agregue o elimine entidades de ellas. – Pando

+0

Es posible que el comportamiento haya cambiado desde que escribí mi pregunta hace 2 años. –

3

pregunta Viejo ...

clase

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 se puede ver que la excepción planteada en la clase proxy en ExtityFramework 5. Estos medios ese comportamiento todavía existe.

Cuestiones relacionadas