Почему моя коллекция прокси Entity Framework Code First не имеет значения и почему я не могу установить ее? - PullRequest
40 голосов
/ 01 ноября 2010

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

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

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

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

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

Вот класс DanceEvent:

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

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

Ответы [ 3 ]

48 голосов
/ 28 марта 2012

Как вы правильно заметили в ответе на свой вопрос, удаление «виртуального» ключевого слова из свойств коллекции позволяет обойти эту проблему, не позволяя Entity Framework создавать прокси-сервер отслеживания изменений.Однако это не решение для многих людей, потому что прокси отслеживания изменений может быть очень удобным и может помочь предотвратить проблемы, когда вы забудете обнаруживать изменения в нужных местах в вашем коде.измените ваши классы POCO, чтобы они создавали свойства коллекции в своем методе доступа get, а не в конструкторе.Вот ваш класс POCO, модифицированный для разрешения создания прокси-сервера отслеживания изменений:

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

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

13 голосов
/ 01 ноября 2010

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

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

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

Iтакже нашел этот ответ от Роуэн Миллер на форуме 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);

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

3 голосов
/ 06 августа 2013

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

Класс 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. Это означает, что поведение все еще существует.

...