6

Ho un modello di entità con User e Person entità, in modo tale che ogni User deve essere associato a esattamente 1 Person, e ciascuno Person può essere associato a zero o 1 User.DbUpdateException con entità che non espone le proprietà di chiave esterna

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

L'associazione è mappata correttamente. Inizialmente ho avuto solo ha dichiarato sul lato 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) 
     ; 

Da quando ho incontrato questo errore, ho anche aggiunto la stessa mappatura a lato 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); 

C'è uno scenario nell'applicazione in cui è possibile creare un nuovo User associato a uno Person esistente. Questo è dove sto avendo difficoltà al momento. EF sta considerando User come il lato dipendente della relazione e sta inserendo una colonna PersonId (FK, int, not null) nella tabella User. Non credo sia possibile utilizzare una proprietà di chiave esterna su entrambe le entità per aiutare EF a gestire l'associazione (vero?).

Ecco po 'di codice difetto che cerca di gestire lo scenario:

// 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(); 
} 

Questo codice funziona bene quando person è nullo (non esiste già nel db). Tuttavia, quando person non è nullo (esiste già in db) e user è nullo, il codice tenta di creare un nuovo User e associarlo allo Person esistente. Quando si richiama SaveChanges(), ottengo un DbUpdateException:

è verificato un errore durante il salvataggio entità che non espongono stranieri chiave proprietà per le loro relazioni. La proprietà EntityEntries restituirà il valore restituendo null poiché non è possibile identificare una singola entità come origine dell'eccezione. La gestione delle eccezioni durante il salvataggio può essere semplificata con esponendo le proprietà delle chiavi esterne nei tipi di entità. Vedi InnerException per i dettagli.

L'eccezione interna è:

una relazione nelle 'User_Person' AssociationSet è nello stato 'soppresso'. Dati i vincoli di molteplicità, anche lo ' ' User_Person_Source 'deve essere nello stato' Eliminato '.

Questo non ha alcun senso per me perché io non sto cercando di eliminare qualsiasi cosa, e controllando il EntityState sia User e Person dimostra che User si trova nello stato Added, mentre Person si trova nello stato Unchanged . Ho ignorato SaveChanges() dimostrare:

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(); 
} 

Quando questo codice viene eseguito, né InvalidOperationException è gettato.Anche in questo caso, userChanges è nello stato Added e personChanges è nello stato Unchanged.

Cosa sto sbagliando?

risposta

6

Mi sento davvero stupido in questo momento. Dopo aver scritto questa domanda attenta, ora è ovvio.

Il Person I test con esiste già e ha già un'associazione User con un diverso User.Name. Questo è il motivo per cui user sta arrivando nullo. Se si imposta la proprietà Person.User su un nuovo User, lo stato User viene inserito nello stato Deleted. Doh.

Ci scusiamo per aver perso tempo. Lascerò la domanda a meno che non sia stato concordato che sarebbe meglio eliminarlo.

+0

Quindi, come si ottiene questo? Ho un problema simile con un argomento che ha un'ultima associazione post. Se si imposta l'ultimo post su un altro post, si tenta di eliminare quello precedente !! WTF è questo? – leen3o

+2

È * circa * il tempo di passare da OR a NHibernate! J/K ... Nel mio caso è stato a causa dei vincoli di molteplicità. Ogni utente ** DEVE AVERE ** una persona, ma una persona può avere un utente nullo. Quando imposto Person.User su null, EF controlla prima i vincoli per vedere se l'altro lato della relazione (User.Person) può essere impostato su null. Poiché non è possibile, EF inserisce l'utente nello stato eliminato. Se avessi impostato User.Person su null, Person ** non sarebbe stato messo nello stato cancellato, dal momento che può avere 0..1 Utente. Se i tuoi vincoli sono diversi, pubblica una domanda e darò un'occhiata. – danludwig

0

Assicurati di avere il rapporto definito nella mappatura (Tipo entità di configurazione)

Ad esempio:

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

Problemi correlati