Я использую DBContext и у меня есть два класса, свойства которых все виртуальные. В отладчике я вижу, что при запросе контекста я получаю прокси-объект. Однако свойство коллекции все еще равно нулю, когда я пытаюсь добавить к нему. Я думал, что прокси обеспечит инициализацию коллекции.
Поскольку мой объект Poco можно использовать вне контекста данных, я добавил проверку на то, что коллекция является нулевой в конструкторе, и создайте ее при необходимости:
public class DanceStyle
{
public DanceStyle()
{
if (DanceEvents == null)
{
DanceEvents = new Collection<DanceEvent>();
}
}
...
public virtual ICollection<DanceEvent> DanceEvents { get; set; }
}
Это работает вне контекста данных, но если я извлекаю объект с помощью запроса, хотя тест является истинным, то при попытке его установить я получаю следующее исключение: «Свойство« DanceEvents »с типом« DanceStyle_B6089AE40D178593955F1328A70EAA3D8F0F01DDE9F9FBD615F60A34F917 может быть установлено, потому что beB не может быть установлен, потому что beB не может быть установлен, потому что beB не может быть установлен, потому что beB не может быть установлен, потому что bebe может быть установлен, так как beB не может быть установлен. коллекция уже установлена на EntityCollection. '
Я вижу, что оно пустое, и я не могу добавить к нему, но я также не могу установить его для коллекции, потому что прокси-сервер говорит, что он уже установлен. Поэтому я не могу использовать это. Я не совсем понимаю.
Вот класс DanceEvent:
public class DanceEvent
{
public DanceEvent()
{
if (DanceStyles == null)
{
DanceStyles = new Collection<DanceStyle>();
}
}
...
public virtual ICollection<DanceStyle> DanceStyles { get; set; }
}
Я пропустил другие свойства типа значения из кода выше. У меня нет других отображений для этих классов в классе контекста.
Я нашел решение этой проблемы здесь: Код Первое добавление в коллекции? Как использовать Code First с репозиториями?
Я удалил «виртуальный» из всех свойств, кроме коллекций и ленивых загруженных объектов, то есть всех собственных типов.
Но я до сих пор не понимаю, как вы можете получить ситуацию, когда у вас есть нулевая коллекция, которую вы не можете использовать, и у вас нет возможности установить ее в действительную коллекцию.
Я также нашел этот ответ от Роуэн Миллер на форуме 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);
Я все еще в замешательстве, хотя мой код теперь работает из-за вышеуказанного исправления.
Как вы правильно заметили в ответе на свой вопрос, удаление «виртуального» ключевого слова из свойств коллекции позволяет обойти эту проблему, не позволяя 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 больше не является автоматическим, а имеет поле поддержки. Лучше, если вы оставите установщик защищенным, не позволяя никакому коду (кроме прокси) впоследствии изменять эти свойства. Вы заметите, что конструктор больше не был необходим и был удален.