Entity Framework множественные объектные контексты - PullRequest
5 голосов
/ 17 апреля 2011

Этот вопрос задавали 500 раз 50 разным способом ... но опять же, так как я не могу найти ответ, который ищу:

Я использую EF4 с прокси POCO.

A. У меня есть график объектов, которые я извлек из одного экземпляра ObjectContext. Этот ObjectContext расположен.

B. У меня есть объект, который я извлек из другого экземпляра ObjectContext. Этот ObjectContext также был удален.

Я хочу установить связанное свойство для нескольких вещей из A, используя сущность в B .... что-то вроде

foreach(var itemFromA in collectionFromA)
{
   itemFromA.RelatedProperty = itemFromB;
}

Когда я это делаю, я получаю исключение:

System.InvalidOperationException occurred
  Message=The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects.
  Source=System.Data.Entity
  StackTrace:
       at System.Data.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedTarget, Boolean applyConstraints, Boolean addRelationshipAsUnchanged, Boolean relationshipAlreadyExists, Boolean allowModifyingOtherEndOfRelationship, Boolean forceForeignKeyChanges)
       at System.Data.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedEntity, Boolean applyConstraints)
       at System.Data.Objects.DataClasses.EntityReference`1.set_ReferenceValue(IEntityWrapper value)
       at System.Data.Objects.DataClasses.EntityReference`1.set_Value(TEntity value)
       at 

Полагаю, мне нужно отсоединить эти сущности от ObjectContexts, когда они располагаются, для того, чтобы вышеприведенное сработало ... Проблема в том, что отсоединение всех сущностей от моего ObjectContext, когда он располагает, кажется, разрушает граф. Если я сделаю что-то вроде:

objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged)  
.Select(i => i.Entity).OfType<IEntityWithChangeTracker>().ToList()  
.ForEach(i => objectContext.Detach(i));

Кажется, что все отношения на графике не установлены.

Как мне решить эту проблему?

Ответы [ 4 ]

11 голосов
/ 17 апреля 2011

@ Дэнни Варод прав. Вы должны использовать один ObjectContext для всего рабочего процесса. Более того, поскольку ваш рабочий процесс выглядит как одна логическая функция, содержащая несколько окон, он, вероятно, также должен использовать один докладчик. Тогда вы будете следовать рекомендуемому подходу: один контекст на докладчика Вы можете звонить SaveChanges несколько раз, чтобы это не нарушало вашу логику.

Источником этой проблемы является хорошо известная проблема с недостатком динамических прокси, сгенерированных поверх сущностей POCO, в сочетании с методами Fixup, сгенерированными шаблоном POCO T4. Эти прокси все еще содержат ссылку на контекст, когда вы его удаляете. Из-за этого они думают, что они все еще привязаны к контексту, и их нельзя привязать к другому контексту. Единственный способ заставить их освободить ссылку на контекст - это ручное отключение. В то же время, как только вы отсоединяете сущность от контекста, она удаляется из связанных прикрепленных сущностей, потому что вы не можете смешивать присоединенные и отсоединенные сущности на одном графике.

На самом деле проблема не возникает в коде, который вы звоните:

itemFromA.RelatedProperty = itemFromB;

но в обратной операции, вызванной методом Fixup:

itemFromB.RelatedAs.Add(itemFromA);

Я думаю, что пути для решения этой проблемы:

  • Не делайте этого и используйте один контекст для всей единицы работы - это предполагаемое использование.
  • Удалите свойство обратной навигации, чтобы метод Fixup не вызывал этот код.
  • Не используйте шаблон POCO T4 с методами Fixup или модифицируйте шаблон T4, чтобы они не генерировались.
  • Отключите отложенную загрузку и создание прокси для этих операций. Это удалит динамические прокси из ваших POCO, и поэтому они будут независимы от контекста.

Чтобы отключить создание прокси и отложенную загрузку, используйте:

var context = new MyContext();
context.ContextOptions.ProxyCreationEnabled = false;

На самом деле вы можете попытаться написать собственный метод для отделения целого графа объектов, но, как вы сказали, его просили 500 раз, и я пока не видел рабочего решения - кроме сериализации и десериализации для нового графа объектов.

5 голосов
/ 17 апреля 2011

Я думаю, у вас есть несколько разных вариантов, 2 из них:

  1. Оставьте контекст живым, пока вы не закончили с процессом, используйте только 1 контекст, а не 2.

  2. а. Перед удалением контекста # 1 создайте глубокий клон графа, используя BinaryStreamer или такой инструмент, как ValueInjecter или AutoMapper.

    б. Объединить изменения из контекста # 2 в клонированный граф.

    с. После сохранения объедините изменения из клонированного графа в граф, созданный новым ObjectContext.

<Ч />

Для дальнейшего использования, эта ссылка на блоги MSDN может помочь вам решить, что делать, когда: http://blogs.msdn.com/b/dsimmons/archive/2008/02/17/context-lifetimes-dispose-or-reuse.aspx

3 голосов
/ 17 апреля 2011

Я не думаю, что вам нужно отсоединиться, чтобы решить проблему.

Мы делаем что-то вроде этого:

public IList<Contact> GetContacts()
{
  using(myContext mc = new mc())
  {
    return mc.Contacts.Where(c => c.City = "New York").ToList();
  }
}

public IList<Sale> GetSales()
{ 
  using(myContext mc = new mc())
  {
    return mc.Sales.Where(c => c.City = "New York").ToList();
  }  
}

public void SaveContact(Contact contact)
{
    using (myContext mc = new myContext())
    {
       mc.Attach(contact);
       contact.State = EntityState.Modified;
       mc.SaveChanges();
    }
}

public void Link()
{
   var contacts = GetContacts();
   var sales = GetSales();

   foreach(var c in contacts)
   {
       c.AddSales(sales.Where(s => s.Seller == c.Name));
       SaveContact(c);
   }
}

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

Важно помнить, что если вы используете IEnumerables, они являются отложенным выполнением,Это означает, что они на самом деле не извлекают информацию, пока вы не сделаете подсчет или не выполните итерацию по ним.Поэтому, если вы хотите использовать его вне контекста, вы должны выполнить ToList (), чтобы он перебирался и создавался список.Затем вы можете работать с этим списком.

РЕДАКТИРОВАТЬ Обновлен, чтобы быть более понятным, благодаря вкладу @ Ника.

0 голосов
/ 17 апреля 2011

Хорошо, я понял, что ваш объектный контекст давно исчез.

Но давайте посмотрим на это так, Entity Framework реализует концепцию единиц работы, в которой он отслеживает изменения, которые вы делаете в графе объектов, чтобы он мог генерировать SQL, соответствующий внесенным изменениям.Без привязки к контексту, он не сможет справиться с изменениями.

Если у вас нет контроля над контекстом, тогда я не думаю, что вы можете что-то сделать.

В противном случае есть две опции:

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

Но даже в случае самостоятельного отслеживания у вас могут возникнуть небольшие проблемы.

...