6

que tiene un modelo de entidad con User y Person entidades, de forma que cada User debe estar asociado con exactamente 1 Person, y cada Person pueden asociar cero o 1 User.DbUpdateException con entidades que no exponen propiedades de clave externa

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

La asociación está mapeada con fluidez. Al principio yo sólo tenía que declaró en el lado 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) 
     ; 

Desde que encontré este error, también añade la misma asignación a un lado 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); 

Hay un escenario en la aplicación donde Se puede crear un nuevo User y asociarlo con un Person existente. Aquí es donde estoy teniendo dificultades en este momento. EF está considerando User como el lado dependiente de la relación, y está colocando una columna PersonId (FK, int, not null) en la tabla User. No creo que sea posible usar una propiedad de clave externa en ninguna entidad para ayudar a EF a administrar la asociación (¿verdad?).

Aquí hay un código en su defecto intenta controlar el escenario:

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

Este código funciona bien cuando person es nula (no existe en el PP). Sin embargo, cuando person no es nulo (ya existe en db) y user es nulo, el código intenta crear un nuevo User y asociarlo con el Person existente. Cuando se invoca SaveChanges(), consigo un DbUpdateException:

Se ha producido un error al guardar las entidades que no expongan extranjeros clave propiedades de sus relaciones. La propiedad EntityEntries será return null porque no se puede identificar una sola entidad como fuente de la excepción. El manejo de excepciones mientras se guarda puede hacerse más fácil al al exponer propiedades de clave foránea en sus tipos de entidad. Vea la InnerException para más detalles.

la excepción interna es:

Una relación de la 'User_Person' AssociationSet está en el estado 'Deleted'. Dadas las restricciones de multiplicidad, un ' ' User_Person_Source 'correspondiente también debe estar en estado' Eliminado '.

Esto no tiene ningún sentido para mí, porque yo no estoy tratando de eliminar cualquier cosa, y la comprobación de la EntityState de ambos User y Person muestra que User está en el estado Added, mientras que Person está en el estado Unchanged . He anulado SaveChanges() demostrar:

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

Cuando este código se ejecuta, ni InvalidOperationException es lanzado.Nuevamente, userChanges está en el estado Added, y personChanges está en el estado Unchanged.

¿Qué estoy haciendo mal?

Respuesta

6

Me siento realmente tonto en este momento. Después de escribir esta pregunta cuidadosa, ahora es obvio.

El Person Estoy probando con que ya existe, y ya tiene una asociación User con un User.Name diferente. Esta es la razón por la cual user viene nulo. Establecer la propiedad Person.User en un nuevo User hace que el antiguo User se ponga en el estado Deleted. Doh.

Perdón por haber perdido su tiempo. Dejaré la pregunta a menos que esté de acuerdo en que sería mejor eliminarla.

+0

Entonces, ¿cómo se puede solucionar esto? Tengo un problema similar con un tema que tiene una última asociación de publicaciones. Si establece la última publicación en una publicación diferente, ¡intenta eliminar la anterior! WTF es eso? – leen3o

+2

¡Es * aproximadamente * hora de cambiar los ORM de EF a NHibernate! J/K ... En mi caso fue por las restricciones de multiplicidad. Cada Usuario ** DEBE TENER ** una Persona, pero una Persona puede tener un Usuario nulo. Cuando configuro Person.User como nulo, EF primero comprueba las restricciones para ver si el otro lado de la relación (User.Person) puede establecerse como nulo. Como no puede, EF pone al Usuario en el estado eliminado. Si estableciera User.Person nulo, Person ** not ** no se habría puesto en el estado eliminado, ya que puede tener 0..1 Usuario. Si sus limitaciones son diferentes, publique una pregunta y la veré. – danludwig

0

Asegúrate de que tienes la relación definida en su asignación (Configuración del tipo de entidad)

Por ejemplo:

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

Cuestiones relacionadas