2013-03-07 23 views
17

Я прочитал несколько вопросов/ответов, связанных с этим конкретным сообщением об ошибке, но я не совсем понимаю подходящее решение.Связь между двумя объектами не может быть определена, поскольку они привязаны к различным объектам ObjectContext.

Я много раз читал, что вы должны создать контекст EF4, использовать его, а затем избавиться от него. Во всем моем приложении я загружаю объекты здесь и там, используя разные объекты контекста, а затем в конечном итоге хочу связать сущности вместе.

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

Как я могу получить два разных объекта для совместного использования одного и того же контекста? Должен ли я действительно создавать новый контекст, загружать два объекта снова (хотя у меня их уже есть), просто связать их и сохранить?

Если я просто пропустил уже существующий, соответствующий вопрос/ответ, пожалуйста, укажите мне нужное место.

Simple EF4 Model diagram

internal class Program { 

    private static void Main(string[] args) { 
     DeleteAllEntities(); 
     CreateInitialEntities(); 
     Owner o = LoadOwner(); 
     Child c = LoadChild(); 
     AssociateAndSave(o, c); 
    } 

    private static void AssociateAndSave(Owner o, Child c) { 
     using (var context = new ModelEntities()) { 
      // Exception occurs on the following line. 
      o.Children.Add(c); 
      context.Attach(o); 
      context.SaveChanges(); 
     } 
    } 

    private static Owner LoadOwner() { 
     using (var context = new ModelEntities()) { 
      return ((from o in context.Owners 
        select o).First()); 
     } 
    } 

    private static Child LoadChild() { 
     using (var context = new ModelEntities()) { 
      return ((from c in context.Children 
        select c).First()); 
     } 
    } 

    private static void CreateInitialEntities() { 
     using (var context = new ModelEntities()) { 
      Owner owner = new Owner(); 
      Child child = new Child(); 
      context.Owners.AddObject(owner); 
      context.Children.AddObject(child); 
      context.SaveChanges(); 
     } 
    } 

    private static void DeleteAllEntities() { 
     using (var context = new ModelEntities()) { 
      List<Child> children = (from c in context.Children 
            select c).ToList(); 
      foreach (var c in children) 
       context.Children.DeleteObject(c); 
      List<Owner> owners = (from o in context.Owners 
            select o).ToList(); 
      foreach (var o in owners) 
       context.Owners.DeleteObject(o); 
      context.SaveChanges(); 
     } 
    } 
} 
+1

Хмм .. Просто называть 'context.Attach (o); context.Attach (c); 'предотвращает ошибку. Но когда я попробовал это в своем фактическом приложении, я получил «Объект с тем же ключом уже существует в ObjectStateManager. ObjectStateManager не может отслеживать несколько объектов с одним и тем же ключом, даже если объекты были загружены аналогично этому примеру ... – Steve

ответ

15

Вы должны получить ссылку на объекты child и owner в том же контексте. Подходом для этого было бы получить идентификаторы, а затем передать их как параметры в метод AssociateAndSave(int oId, int cId).

Затем вы извлекаете ссылки на объекты в том же контексте, и вы делаете вложение.

private static void Main(string[] args) 
{ 
    DeleteAllEntities(); 
    CreateInitialEntities(); 
    int oId = LoadOwnerId(); 
    int cId = LoadChildId(); 
    AssociateAndSave(oId, cId); 
} 

private static int LoadOwnerId() 
{ 
    using (var context = new ModelEntities()) 
    { 
     return (from o in context.Owners 
       select o).First().Id; 
    } 
} 

private static int LoadChildId() 
{ 
    using (var context = new ModelEntities()) 
    { 
     return (from c in context.Children 
       select c).First().Id; 
    } 
} 

private static void AssociateAndSave(int oId, int cId) 
{ 
    using (var context = new ModelEntities()) 
    { 
     var owner = (from o in context.Owners 
         select o).FirstOrDefault(o => o.ID == oId); 
     var child = (from o in context.Children 
         select o).FirstOrDefault(c => c.ID == cId); 

     owner.Children.Add(child); 
     context.Attach(owner); 
     context.SaveChanges(); 
    } 
} 
+1

Это хорошее решение, хотя он не будет работать в EF 4.0. – Freeman

+2

Я действительно начал это делать, но я решил, что было немного расточительно запрашивать базу данных для объекта, который я уже загрузил ранее из базы данных. – Steve

+1

Я решил пойти с частями обоих ответов на этот вопрос, но, к сожалению, я не могу отметить их как ответы .. :) Любой, кто ищет, находит это также должен увидеть http://stackoverflow.com/a/5695009/425871 , – Steve

3

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

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

EX:

private static void CrudOperationExample(DBContext contextInstane) 
{ 
    //perform crud operations. 
} 

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

+1

Я использовал для сохранения контекста, чтобы я мог это сделать, но снова и снова я читал, что вы должны только держите контекст в течение очень короткого времени, когда вам это нужно. В моем приложении возможно, что контекст может быть открыт в течение 5 минут или, может быть, даже часа. Это будет действительно зависеть от того, как долго пользователь оставил окно открытым. – Steve

+1

Кроме того, вы говорите, что каждый контекст отслеживает свои объекты отдельно, но контекст был удален (все мои операции находятся в 'использовании' блоков). – Steve