6

Mam modelu Podmiot o User i Person podmiotów, tak, że każdy User musi być powiązany z dokładnie 1 Person i każdy Person może być związane zero lub 1 User.DbUpdateException z podmiotami, które nie narazić klucz obcy właściwości

User (0..1) <-------> (1) Person 

Stowarzyszenie jest mapowane płynnie. Początkowo miałem tylko on podany na stronie Person:

private class PersonOrm : EntityTypeConfiguration<Person> 
{ 
    internal PersonOrm() 
    { 
     ToTable(typeof(Person).Name, DbSchemaName.People); 

     // has zero or one User 
     HasOptional(p => p.User) 
      .WithRequired(d => d.Person) 
      .Map(d => d.MapKey("PersonId")) 
      .WillCascadeOnDelete(false) 
     ; 

Odkąd napotkał ten błąd, ja również dodaje ten sam mapowanie do boku User:

private class UserOrm : EntityTypeConfiguration<User> 
{ 
    internal UserOrm() 
    { 
     ToTable(typeof(User).Name, DbSchemaName.Identity); 

     // has exactly 1 Person 
     HasRequired(p => p.Person) 
      .WithOptional(d => d.User) 
      .Map(d => d.MapKey("PersonId")) 
      .WillCascadeOnDelete(false); 

Jest to scenariusz w aplikacji, w której nowy User może zostać utworzony i powiązany z istniejącym Person. Właśnie w tym momencie mam trudności. EF bierze pod uwagę User jako zależną stronę relacji i umieszcza kolumnę PersonId (FK, int, not null) na tabeli . Nie wierzę, że można użyć własności klucza obcego w obu jednostkach, aby pomóc EF zarządzać powiązaniem (czy tak?).

Oto kod wadą, która próbuje poradzić sobie scenariusz:

// find out if Person already exists 
var person = context.People.SingleOrDefault(p => p.Emails.Any(
    e => e.Value.Equals(emailAddress, StringComparison.OrdinalIgnoreCase))); 

// find out if User already exists 
var user = context.Users.SingleOrDefault(
    u => u.Name.Equals(emailAddress, StringComparison.OrdinalIgnoreCase)); 

if (user == null) 
{ 
    user = new User 
    { 
     Name = emailAddress, 
     IsRegistered = isRegistered, 
     Person = person ?? PersonFactory.Create(emailAddress), 
     // ignore the PersonFactory.Create, that part works 
    }; 

    context.Entry(user).State = EntityState.Added; 
    context.SaveChanges(); 
} 

Ten kod działa poprawnie, gdy person jest zerowy (nie istnieją już w db). Jednak gdy person nie jest null (już istnieje w db) i user ma wartość null, kod próbuje utworzyć nowy User i skojarzyć go z istniejącym Person. Podczas wywoływania SaveChanges(), otrzymuję DbUpdateException:

Wystąpił błąd podczas zapisywania podmioty, które nie narażają klucz obcy właściwości ich relacji. Właściwość EntityEntries zwróci wartość null , ponieważ nie można zidentyfikować pojedynczej encji jako źródła wyjątku. Obsługa wyjątków podczas zapisywania może być łatwiejsza dzięki ujawnieniu właściwości klucza obcego w typach jednostek. Szczegółowe informacje można znaleźć w artykule InnerException pod adresem .

Wewnętrzna wyjątek:

Związek z określenia 'User_Person' AssociationSet jest w stanie 'usunięta. Biorąc pod uwagę ograniczenia wielokrotności, odpowiedni " " User_Person_Source "również musi znajdować się w stanie" Usunięte ".

To nie ma żadnego sensu dla mnie, bo ja nie próbuję niczego usuwać, i sprawdzanie EntityState zarówno User i Person pokazuje, że User jest w stanie Added, natomiast Person jest w stanie Unchanged . Mam nadpisane SaveChanges() wykazać:

public override int SaveChanges() 
{ 
    var userChanges = ChangeTracker.Entries<User>().Single(); 
    var personChanges = ChangeTracker.Entries<Person>().Single(); 

    if (userChanges.State == EntityState.Deleted) 
     throw new InvalidOperationException("wtf?"); 

    if (personChanges.State == EntityState.Deleted) 
     throw new InvalidOperationException("wtf?"); 

    return base.SaveChanges(); 
} 

Po wykonaniu tego kodu, ani InvalidOperationException jest wyrzucane.Ponownie userChanges znajduje się w stanie Added, a personChanges znajduje się w stanie .

Co robię źle?

Odpowiedz

6

Czuję się teraz naprawdę głupi. Po napisaniu tego ostrożnego pytania, jest to teraz oczywiste.

Testuję z już istnieje i ma już skojarzenie User z innym User.Name. Właśnie dlatego user zbliża się do zera. Ustawienie właściwości Person.User na nową User powoduje, że stary User należy umieścić w stanie . Doh.

Przepraszam, że zmarnowałem twój czas. Pozostawię to pytanie, chyba że zgodzi się, że lepiej go usunąć.

+0

Jak sobie z tym poradzić? Mam podobny problem z tematem, który ma ostatnie skojarzenie postu. Jeśli ustawisz ostatni post na inny post, spróbuje usunąć poprzednie! WTF o to chodzi? – leen3o

+2

To * około * czasu na przełączenie ORM z EF na NHibernate! J/K ... W moim przypadku wynikało to z ograniczeń wielokrotności. Każdy użytkownik ** MUSI MIEĆ ** Osobę, ale Osoba może mieć NULL Użytkownika. Kiedy ustawię Person.User na null, EF najpierw sprawdza ograniczenia, aby sprawdzić, czy druga strona związku (User.Person) może być ustawiona na wartość null. Ponieważ nie może, EF umieszcza Użytkownika w usuniętym stanie. Gdybym ustawił użytkownika na wartość null, osoba ** nie ** zostałaby umieszczona w stanie usuniętym, ponieważ może mieć 0..1 użytkownika. Jeśli twoje ograniczenia są różne, wyślij pytanie, a ja popatrzę. – danludwig

0

Upewnij się, że związek określony w mapowaniu (typ Entity Configuration)

Na przykład:

this.HasRequired (t => t.QuoteResponse) .WithMany (t => t.QuoteResponseItems) .HasForeignKey (d => d.QuoteResponseID);