Por que meu código de estrutura da entidade é nulo First Proxy e por que não posso defini -la?

StackOverflow https://stackoverflow.com/questions/4069563

Pergunta

Estou usando o DBContext e tenho duas classes cujas propriedades são todas virtuais. Eu posso ver no depurador que estou recebendo um objeto de proxy quando consultar o contexto. No entanto, uma propriedade de coleção ainda é nula quando tento adicioná -la. Eu pensei que o proxy garantiria que a coleção fosse inicializada.

Como meu objeto Poco pode ser usado fora do contexto de dados, adicionei uma verificação para a coleção ser nula no construtor e criá -lo, se necessário:

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

That works outside the data context but if I retrieve an object using a query, although the test is true, when I try to set it, I get following exception: 'The property 'DanceEvents' on type 'DanceStyle_B6089AE40D178593955F1328A70EAA3D8F0F01DDE9F9FBD615F60A34F9178B94' cannot be set because the A coleção já está definida como uma entidade -collection. '

Eu posso ver que é nulo e não posso adicionar a ele, mas também não posso defini -lo como uma coleção porque o proxy diz que já está definido. Portanto, não posso usá -lo. Estou confuso.

Aqui está a aula de dancevent:

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

Eu omiti as outras propriedades do tipo de valor do código acima. Não tenho outros mapeamentos para essas classes na classe de contexto.

Foi útil?

Solução 2

Eu encontrei a solução para este problema aqui: Código primeiro adicionando coleções? Como usar o código primeiro com repositórios?

Eu removi 'virtual' de todas as propriedades, exceto coleções e objetos carregados preguiçosos, ou seja, todos os tipos nativos.

Mas ainda não entendo como você pode acabar com a situação em que tem uma coleção nula que não pode usar e não tem como defini -la como uma coleção válida.

Eu também encontrei Esta resposta de Rowan Miller Em um fórum MSDN

Oi,

Se você tornar todas as suas propriedades virtuais, a EF gerará classes de proxy em tempo de execução que deriva da sua classificação POCO, esses proxies permitem que a EF descubra as mudanças em tempo real, em vez de ter que capturar os valores originais do seu objeto e depois digitalizar as alterações Quando você salva (isso obviamente tem benefícios de desempenho e uso de memória, mas a diferença será insignificante, a menos que você tenha um grande número de entidades carregadas na memória). Eles são conhecidos como 'proxies de rastreamento de alterações', se você tornar suas propriedades de navegação virtual, um proxy ainda será gerado, mas é muito mais simples e inclui apenas uma lógica para executar o carregamento preguiçoso quando você acessa uma propriedade de navegação.

Como seu código original estava gerando proxies de rastreamento de alterações, a EF estava substituindo sua propriedade de coleção por um tipo de coleção especial para ajudá -lo a descobrir as alterações. Porque você tenta definir a coleção de volta para uma lista simples no construtor, está obtendo a exceção.

A menos que você esteja vendo problemas de desempenho, eu seguiria a sugestão de Terrence e apenas removeria 'virtual' de suas propriedades não de navegação.

~ Rowan

Parece que eu só tenho o problema com um 'proxy de rastreamento de mudança' completo se todas as minhas propriedades forem virtuais. Mas, considerando isso, por que ainda não posso usar a propriedade virtual no proxy de rastreamento de alterações? Este código explode na linha três porque o DS2.DanceEvents é nulo e não pode ser definido no construtor:

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

Ainda estou confuso, mesmo que meu código agora esteja funcionando por causa da correção acima.

Outras dicas

Como você observou corretamente na resposta à sua própria pergunta, a remoção da palavra -chave "virtual" das propriedades da coleção funciona em torno do problema, impedindo que a estrutura da entidade crie um proxy de rastreamento de alterações. No entanto, essa não é uma solução para muitas pessoas, porque os proxies de rastreamento de alterações podem ser realmente convenientes e podem ajudar a evitar problemas quando você esquece de detectar alterações nos lugares certos do seu código.

Uma abordagem melhor seria modificar suas classes POCO, para que elas instanciem as propriedades da coleta em seu acessador, e não no construtor. Aqui está a sua classe POCO, modificada para permitir a criação de proxy de rastreamento de alterações:

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

No código acima, a propriedade Coleção não é mais automática, mas possui um campo de apoio. É melhor se você deixar o setter protegido, impedindo que qualquer código (exceto o proxy) modifique posteriormente essas propriedades. Você notará que o construtor não era mais necessário e foi removido.

Pergunta velha ...

Classe Poco:

public partial class MyPOCO
{
    public MyPOCO()
    {
        this.MyPocoSub = new HashSet<MyPocoSub>();
    }

    //VIRTUAL
    public virtual ICollection<MyPocoSub> MyPocoSub { get; set; }
}

e código de 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 você pode ver essa exceção levantada na classe de proxy no ExtityFramework 5. Isso significa que ainda existe o comportamento.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top