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

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

Вопрос

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

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

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

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

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

Вот класс DanceEvent:

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

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

Это было полезно?

Решение 2

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

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

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

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

Привет,

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

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

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

~Роуэн

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

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

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

Другие советы

Как вы правильно заметили в ответе на свой собственный вопрос, удаление ключевого слова "virtual" из свойств коллекции решает проблему, не позволяя 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 больше не является автоматическим, а скорее имеет резервное поле.Будет лучше, если вы оставите установщик защищенным, предотвращая последующее изменение этих свойств любым кодом (кроме прокси).Вы заметите, что конструктор больше не был нужен и был удален.

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top