Подтвердить что ты не робот

Почему мой код Entity Framework Code First proxy null и почему я не могу его установить?

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

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

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

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

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

Вот класс DanceEvent:

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

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

4b9b3361

Ответ 1

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

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

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

Я также нашел этот ответ от Rowan Miller на форуме MSDN

Привет,

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

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

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

~ Роуэн

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

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

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

Ответ 2

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

Лучшим подходом было бы изменить ваши классы POCO, чтобы они инстанцировали свойства коллекции в свой get accessor, а не в конструкторе. Здесь ваш класс POCO, измененный для создания прокси-сервера изменения отслеживания:

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

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

Ответ 3

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

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