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?
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
¡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