Почему моя коллекция прокси Entity Framework Code First не имеет значения и почему я не могу установить ее?

Я использую DBContext и у меня есть два класса, свойства которых все виртуальные. В отладчике я вижу, что при запросе контекста я получаю прокси-объект. Тем не менее, свойство коллекции по-прежнему равно нулю, когда я пытаюсь добавить к нему. Я думал, что прокси обеспечит инициализацию коллекции.

Поскольку мой объект Poco можно использовать вне контекста данных, я добавил проверку на то, что коллекция является нулевой в конструкторе, и создайте ее при необходимости:

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

Это работает вне контекста данных, но если я извлекаю объект с помощью запроса, хотя тест является истинным, то при попытке его установить я получаю следующее исключение: "Свойство" DanceEvents "для типа" DanceStyle_B6089AE40D178593955F1328A70EAA3D8F0F01DDE9F9FBD615F60A34F917 может быть установлено как BB8, так как beB не может быть установлен, потому что beB не может быть установлен, потому что beB не может быть установлен, потому что beB не может быть установлен, потому что beB не может быть установлен, потому что beB не может быть установлен. коллекция уже установлена ​​на EntityCollection.'

Я вижу, что оно пустое, и я не могу добавить к нему, но я также не могу установить его в коллекцию, потому что прокси-сервер говорит, что он уже установлен. Поэтому я не могу использовать это. Я не совсем понимаю.

Вот класс DanceEvent:

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

Я опустил другие свойства типа значения из кода выше. У меня нет других отображений для этих классов в классе контекста.

3 ответа

Решение

Я нашел решение этой проблемы здесь: Код Первое добавление в коллекции? Как использовать Code First с репозиториями?

Я удалил "виртуальный" из всех свойств, кроме коллекций и ленивых загруженных объектов, то есть всех собственных типов.

Но я до сих пор не понимаю, как вы можете оказаться в ситуации, когда у вас есть нулевая коллекция, которую вы не можете использовать, и у вас нет возможности установить ее в действительную коллекцию.

Я также нашел этот ответ от Роуэн Миллер на форуме MSDN

Привет,

Если вы сделаете все свои свойства виртуальными, то EF будет генерировать прокси-классы во время выполнения, которые получаются из вашего класса POCO, эти прокси-серверы позволяют EF узнавать об изменениях в реальном времени, а не собирать исходные значения вашего объекта, а затем сканировать на наличие изменений когда вы сохраняете (это, очевидно, имеет преимущества в производительности и использовании памяти, но разница будет незначительной, если в память не загружено большое количество объектов). Они известны как "прокси отслеживания изменений", если вы сделаете свои свойства навигации виртуальными, тогда прокси-сервер все еще генерируется, но он намного проще и просто включает некоторую логику для выполнения отложенной загрузки при доступе к свойству навигации.

Поскольку ваш исходный код генерировал прокси-серверы отслеживания изменений, EF заменял свойство вашей коллекции специальным типом коллекции, чтобы помочь ему узнать об изменениях. Поскольку вы пытаетесь вернуть коллекцию обратно в простой список в конструкторе, вы получаете исключение.

Если вы не видите проблем с производительностью, я бы последовал совету Терренса и просто удалил "виртуальный" из ваших свойств, не связанных с навигацией.

~ Rowan

Таким образом, кажется, что у меня проблема с полным "прокси отслеживания изменений", только если все мои свойства виртуальные. Но, учитывая это, почему я до сих пор не могу использовать виртуальное свойство на прокси-сервере отслеживания изменений? Этот код взрывается в третьей строке, потому что ds2.DanceEvents имеет значение null и не может быть установлен в конструкторе:

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

Я все еще в замешательстве, хотя мой код теперь работает из-за вышеуказанного исправления.

As you correctly observed in the answer to your own question, removing the "virtual" keyword from the collection properties works around the problem, by preventing the Entity Framework from creating a change tracking proxy. Тем не менее, это не решение для многих людей, потому что прокси отслеживания изменений может быть очень удобным и может помочь предотвратить проблемы, когда вы забудете обнаружить изменения в нужных местах в вашем коде.

Лучшим подходом было бы модифицировать ваши классы POCO, чтобы они создавали свойства коллекции в своем методе доступа get, а не в конструкторе. Here's your POCO class, modified to allow change tracking proxy creation:

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

В приведенном выше коде свойство collection больше не является автоматическим, а имеет поле поддержки. Лучше, если вы оставите установщик защищенным, предотвращая последующее изменение этих свойств любым кодом (кроме прокси). Вы заметите, что конструктор больше не был необходим и был удален.

Старый вопрос...

Класс Poco:

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

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

и код прокси:

    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;
        }
    }

Как вы можете видеть, это исключение возникло в прокси-классе в ExtityFramework 5. Это означает, что поведение все еще существует.

Другие вопросы по тегам