Почему мой код 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; }
}
Я опустил другие свойства типа значения из приведенного выше кода. У меня нет других сопоставлений для этих классов в классе контекста.
Ответы
Ответ 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. Это означает, что поведение по-прежнему существует.