entity-framework - update - willcascadeondelete entity framework core
EntityFramewok: cómo configurar Cascade-Delete para anular las claves externas (2)
Funciona de hecho como se describe, pero el artículo en MSDN no hace hincapié en que solo funciona si los elementos secundarios también se cargan en el contexto , no solo la entidad principal. Por lo tanto, en lugar de utilizar Find
(que solo carga el elemento principal), debe usar la carga ansiosa con Include
(o cualquier otra forma de cargar los elementos secundarios en el contexto):
using (var dbContext = new TestContext())
{
var master = dbContext.Set<TestMaster>().Include(m => m.Children)
.SingleOrDefault(m => m.Id == 1);
dbContext.Set<TestMaster>().Remove(master);
dbContext.SaveChanges();
}
Esto eliminará el maestro de la base de datos, configurará todas las claves foráneas en las entidades Child
para null
y escribirá las sentencias UPDATE para los niños en la base de datos.
La documentación de EntityFramework indica que el siguiente comportamiento es posible:
Si una clave externa en la entidad dependiente es nulable, Code First no establece la eliminación en cascada en la relación, y cuando se elimina el principal, la clave externa se establecerá en nulo.
(desde http://msdn.microsoft.com/en-us/jj591620 )
Sin embargo, no puedo lograr tal comportamiento.
Tengo las siguientes Entidades definidas con código primero:
public class TestMaster
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<TestChild> Children { get; set; }
}
public class TestChild
{
public int Id { get; set; }
public string Name { get; set; }
public virtual TestMaster Master { get; set; }
public int? MasterId { get; set; }
}
Aquí está la configuración de asignación de API Fluent:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<TestMaster>()
.HasMany(e => e.Children)
.WithOptional(p => p.Master).WillCascadeOnDelete(false);
modelBuilder.Entity<TestChild>()
.HasOptional(e => e.Master)
.WithMany(e => e.Children)
.HasForeignKey(e => e.MasterId).WillCascadeOnDelete(false);
}
La clave externa es anulable, la propiedad de navegación está asignada como opcional, por lo que espero que la eliminación en cascada funcione como se describe en MSDN, es decir, anular las ID maestras de todos los hijos y luego eliminar el objeto maestro.
Pero cuando intento eliminar, obtengo el error de violación FK:
using (var dbContext = new TestContext())
{
var master = dbContext.Set<TestMaster>().Find(1);
dbContext.Set<TestMaster>().Remove(master);
dbContext.SaveChanges();
}
En SaveChanges () arroja lo siguiente:
System.Data.Entity.Infrastructure.DbUpdateException : An error occurred while updating the entries. See the inner exception for details.
----> System.Data.UpdateException : An error occurred while updating the entries. See the inner exception for details.
----> System.Data.SqlClient.SqlException : The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.TestChilds_dbo.TestMasters_MasterId". The conflict occurred in database "SCM_Test", table "dbo.TestChilds", column ''MasterId''.
The statement has been terminated.
¿Estoy haciendo algo mal o no entendí lo que dice MSDN?
Después de seguir la gran respuesta de @ Slauma aún recibía el mismo error que OP.
Así que no seas tan ingenuo como yo y pienses que los siguientes ejemplos terminarán con el mismo resultado.
dbCtx.Entry(principal).State = EntityState.Deleted;
dbCtx.Dependant.Where(d => d.PrincipalId == principalId).Load();
// code above will give error and code below will work on dbCtx.SaveChanges()
dbCtx.Dependant.Where(d => d.PrincipalId == principalId).Load();
dbCtx.Entry(principal).State = EntityState.Deleted;
Primero, cargue a los niños en contexto antes de establecer el estado de la entidad en eliminado (si lo está haciendo de esa manera).