2010-11-01 5 views
39

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

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

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

Это работает вне контекста данных, но если Я извлекаю объект с помощью запроса, хотя тест является истинным, когда я пытаюсь его установить, я получаю следующее исключение: «Свойство« 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; } 
} 

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

ответ

13

Я нашел решение этой проблемы здесь: Code First adding to collections? How to use Code First with repositories?

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

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

Я также нашел this answer from Rowan Miller на форуме MSDN

Привет,

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

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

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

~ Роуэн

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

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

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

47

Как вы правильно заметили в ответе на свой вопрос, устранение «виртуального» ключевого слова из свойств коллекции работает вокруг проблемы, не позволяя 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; } 
    } 
} 

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

+0

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

+0

Я не могу воспроизвести то, что вы описываете. По моему опыту, когда объект создается как прокси-сервер (либо в результате его возврата по запросу, либо с использованием метода DbSet.Create), его свойства коллекции создаются с объектами EntityCollection. Вам никогда не придется устанавливать эти свойства - просто добавьте/удалите из них объекты. – Pando

+0

Возможно, поведение изменилось с тех пор, как я написал свой вопрос 2 года назад. –

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

 Смежные вопросы

  • Нет связанных вопросов^_^