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

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

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

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

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

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

вот класс DanceEvent:

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

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

3 ответов


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


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

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

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

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

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


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

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