7

Mam następujące dwa modele i DbContext:Entity Framework - Aktualizacja relacji zmieniając klucz obcy

public class TestDbContext : DbContext 
    { 
     public IDbSet<Person> People { get; set; } 
     public IDbSet<Car> Cars { get; set; } 
    } 

    public class Person 
    { 
     public Person() 
     { 
      ID = Guid.NewGuid(); 
     } 

     public Guid ID { get; set; } 
     public string Name { get; set; } 
     public virtual List<Car> Cars { get; set; } 
    } 

    public class Car 
    { 
     public Car() 
     { 
      ID = Guid.NewGuid(); 
     } 

     public Guid ID { get; set; } 
     public string Name { get; set; } 
     public virtual Person Owner { get; set; } 
    } 

I wtedy zadeklarować listę osób i listę samochodów, ustawienie właściciela pierwszego samochodu do pierwsza osoba na liście:

List<Person> People = new List<Person>() 
     { 
      new Person() {Name = "bill", ID = new Guid("6F39CC2B-1A09-4E27-B803-1304AFDB23E3")}, 
      new Person() {Name = "ben", ID = new Guid("3EAE0303-39D9-4FD9-AF39-EC6DC73F630B")} 
     }; 

     List<Car> Cars = new List<Car>() { new Car() { Name = "Ford", Owner = People[0], ID = new Guid("625FAB6B-1D56-4F57-8C98-F9346F1BBBE4") } }; 

Zapisuję to do bazy danych za pomocą następującego kodu i działa dobrze.

using (TestDbContext context = new TestDbContext()) 
     { 
      foreach (Person person in People) 
      { 
       if (!(context.People.Any(p => p.ID == person.ID))) 
        context.People.Add(person); 
       else 
       { 
        context.People.Attach(person); 
        context.Entry<Person>(person).State = System.Data.EntityState.Modified; 
       } 
      } 
      foreach (Car caar in Cars) 
      { 
       if (!(context.Cars.Any(c => c.ID == caar.ID))) 
        context.Cars.Add(caar); 
       else 
       { 
        context.Cars.Attach(caar); 
        context.Entry<Car>(caar).State = System.Data.EntityState.Modified; 
       } 
      } 
      context.SaveChanges(); 
     } 

Jeśli następnie zmienię właściciela samochodu na drugą osobę i ponownie uruchomę kod, właściwość właściciela samochodu nie zostanie zaktualizowana.

Cars[0].Owner = People[1]; 

Jakieś pomysły na to, co robię źle? Dzięki za pomoc.

+0

Aby dodać, próbuję osiągnąć to, co jest zasugerowane http://stackoverflow.com/questions/9382005/move-child-entities-to-a-new-parent-entity – user2697817

+0

Jak to zrobiłeś? Gdzie jest kod? Musisz przeładować 'Cars' z DB, a następnie wykonać zadanie. – zsong

Odpowiedz

2

Uważam, że jest to problem związany z independent vs foreign key association. Korzystasz obecnie z niezależnego powiązania, a relacją między samochodem a osobą zarządza osobny obiekt wpisu, który ma swój własny stan (aby uzyskać dostęp do tego obiektu, musisz użyć interfejsu ObjectContext API). Ustawienie wejścia pojazdu do stanu zmodyfikowanego nie zmieni stanu pozycji dla relacji! Prostym rozwiązaniem jest użycie zamiast tego klucza obcego, co oznacza dodanie do samochodu nowej właściwości Guid PersonId i odwzorowanie jej jako własności klucza obcego dla właściwości nawigacji Person.

Jeśli nalegasz na używanie niezależnych skojarzeń, powinieneś zmienić relacje tylko na załączonych jednostkach, inaczej będziesz miał silny ból głowy ze śledzeniem tych zmian i ustawianiem wszystkich wymaganych wpisów z prawidłowym stanem. Powinno wystarczyć do tworzenia obiektów, dołączania ich do kontekstu i dopiero po tym ustawieniu właściciela samochodu - miejmy nadzieję, że będzie on śledzony jako zmiana.

+0

Dzięki za pomoc, jest to podobne do robienia tego, co jest zasugerowane w tej odpowiedzi http: // stackoverflow.com/questions/5506116/entity-framework-code-first-why-cant-i-update-complex-properties-this-way – user2697817

+0

Tak, powinno być tak samo. –

+0

@LadislavMrnka: Tylko uwaga: naprawdę mam dla ciebie szacunek i doceniam twój wkład w wiele pytań EF ... czy mógłbyś również sprawdzić moje pytanie? http://stackoverflow.com/questions/18529338/entity-framework-controlling-db-connection-and-specifying-own-transaction – Learner

0

spróbować czegoś takiego:

using (TestDbContext context = new TestDbContext()) 
      { 
       foreach (Person person in People) 
       { 
        if (!(context.People.Any(p => p.ID == person.ID))) 
         context.People.Add(person); 
        else 
        { 
         context.People.Attach(person); 
         context.Entry<Person>(person).State = System.Data.EntityState.Modified; 
        } 
        context.SaveChanges(); 
       } 
       foreach (Car caar in Cars) 
       { 
        if (!(context.Cars.Any(c => c.ID == caar.ID))) 
         context.Cars.Add(caar); 
        else 
        { 
         context.Cars.Attach(caar); 
         context.Entry<Car>(caar).State = System.Data.EntityState.Modified; 
        } 
        context.SaveChanges(); 
       } 

      } 

Myślę, że błąd wynika z context.SaveChanges docelowych (i ciowo swoją architekturę). Rozważ użycie dedykowanej metody (na przykład podstawowego CRUD) dla każdej operacji na twoim DB poprzez Entity Framework. Mam nadzieję że to pomoże.

Edit: Z approch CRUD:

public class PersonManager // CRUD 
    { 
     public void Create(Person person) 
     { 
      using (TestDbContext context = new TestDbContext()) 
      { 
       context.Person.Add(person); 
       context.SaveChanges(); 
      } 
     } 

     public void Update(Person person) 
     { 
      using (TestDbContext context = new TestDbContext()) 
      { 
       context.Person.Attach(person); 
       context.Entry(person).State = System.Data.EntityState.Modified; 
       context.SaveChanges(); 
      } 
     } 
    } 

Można również dokonać tej klasy static w celu dopasowania do architektury.

+0

Próbowałem tego podejścia i nadal nie działa. – user2697817

+0

Czy właśnie skopiowałeś/wkleiłeś? A może stworzyłeś podstawowy CRUD? Ponieważ z aplikacją CRUD nie powinieneś mieć błędów ... – Atlasmaybe

+0

Próbowałem tego, co sugerowałeś, to nie działało. Może musisz być bardziej klarowny. :-) – user2697817

Powiązane problemy