2013-03-07 3 views
17

Ho letto alcune domande/risposte che hanno a che fare con questo particolare messaggio di errore, ma non sto capendo la soluzione appropriata.La relazione tra i due oggetti non può essere definita perché sono collegati a diversi oggetti ObjectContext

Ho letto molte volte che è necessario creare il contesto EF4, usarlo, quindi smaltirlo. Durante la mia applicazione, sto caricando le entità qua e là usando diversi oggetti di contesto, e poi eventualmente voglio associare le entità insieme.

Ho creato un'applicazione di console semplice che causa facilmente l'errore. Il modello molto semplice è schematizzato seguito dal codice.

Come è possibile ottenere le due entità diverse per condividere lo stesso contesto? Devo davvero creare un nuovo contesto, caricare nuovamente le due entità (anche se li ho già), semplicemente per associarle e salvare?

Se semplicemente mi sono perso una domanda/risposta già esistente, vi prego di indicarmi il posto giusto.

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

Hmm .. Semplicemente chiamando 'context.Attach (o); context.Attach (c); 'impedisce l'errore. Ma quando ho provato questo nella mia applicazione reale, ho ottenuto un oggetto con la stessa chiave già esistente in ObjectStateManager. ObjectStateManager non può tracciare più oggetti con la stessa chiave anche se le entità sono state caricate in modo simile a questo esempio ... – Steve

risposta

15

Si dovrebbe ottenere un riferimento agli oggetti figlio e proprietario nello stesso contesto. Un approccio per questo sarebbe ottenere gli id ​​e quindi passarli come parametri al metodo AssociateAndSave(int oId, int cId).

Quindi, si recuperano i riferimenti agli oggetti nello stesso contesto e si effettua l'allegato.

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

Questa è una buona soluzione, anche se non funzionerà in EF 4.0 – Freeman

+2

In realtà ho iniziato a farlo, ma ho pensato che fosse un po 'dispendioso interrogare il database per l'entità che avevo già caricato in precedenza dal database. – Steve

+1

Ho deciso di andare con una parte di entrambe le risposte su questa domanda, ma sfortunatamente non riesco a contrassegnarle entrambe come risposte .. :) Qualsiasi ricerca che trova questo dovrebbe anche vedere http://stackoverflow.com/a/5695009/425871 . – Steve

3

Non è possibile eliminare gli oggetti da un contesto, con un'istanza di un altro contesto, perché ogni contesto tiene traccia i suoi oggetti separatamente.

Un'idea sarebbe quella di fornire a ciascuno dei metodi un parametro del tipo di contesto in modo che le operazioni vengano eseguite nello stesso contesto.

EX:

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

mi si vuole fare più bene, vi consiglio di cercare per l'iniezione di dipendenza, in quanto è molto utile per ORM di.

+1

Ho usato per mantenere un contesto vivo in modo da poterlo fare, ma ancora e ancora ho letto che dovresti mantieni il contesto solo per un tempo molto breve, quando ne hai bisogno. È possibile nella mia applicazione che il contesto possa essere aperto per 5 minuti, o forse anche un'ora. Dipenderebbe davvero da quanto tempo l'utente ha tenuto aperta la finestra. – Steve

+1

Inoltre, tu dici che ogni contesto tiene traccia delle sue entità separatamente, ma il contesto è stato eliminato (tutte le mie operazioni sono in "uso" dei blocchi). – Steve