Entity Framework 6: Code First Cascade delete

c# entity-framework entity-framework-6 sql

Pregunta

Así que hay varias preguntas similares aquí a esto, pero todavía tengo problemas para determinar qué es exactamente lo que me estoy perdiendo en mi escenario simplificado.

Digamos que tengo las siguientes tablas, hábilmente nombradas por mi:

'JohnsParentTable' (Id, Description) 
'JohnsChildTable' (Id, JohnsParentTableId, Description)

Con las clases resultantes pareciendo así

public class JohnsParentTable
{
    public int Id { get; set; }
    public string Description { get; set; }
    public virtual ICollection<JohnsChildTable> JohnsChildTable { get; set; }

    public JohnsParentTable()
    {
        JohnsChildTable = new List<JohnsChildTable>();
    }
}

internal class JohnsParentTableConfiguration : EntityTypeConfiguration<JohnsParentTable>
{
    public JohnsParentTableConfiguration()
    {
        ToTable("dbo.JohnsParentTable");
        HasKey(x => x.Id);
        Property(x => x.Id).HasColumnName("Id").IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        Property(x => x.Description).HasColumnName("Description").IsRequired().HasMaxLength(50);
    }
}

public class JohnsChildTable
{
    public int Id { get; set; }
    public string Description { get; set; }
    public int JohnsParentTableId { get; set; }
    public JohnsParentTable JohnsParentTable { get; set; }
}

internal class JohnsChildTableConfiguration : EntityTypeConfiguration<JohnsChildTable>
{
    public JohnsChildTableConfiguration()
    {
        ToTable("dbo.JohnsChildTable");
        HasKey(x => x.Id);
        Property(x => x.Id).HasColumnName("Id").IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        Property(x => x.Description).HasColumnName("Description").IsRequired().HasMaxLength(50);
        HasRequired(a => a.JohnsParentTable).WithMany(c => c.JohnsChildTable).HasForeignKey(a => a.JohnsParentTableId);
    }
}

En la base de datos tengo una fila en la tabla principal con un Id. De 1 junto con dos filas en la tabla secundaria vinculadas a este padre. Si hago esto:

var parent = db.JohnsParentTable.FirstOrDefault(a => a.Id == 1)

El objeto está correctamente poblado. Sin embargo, si intento eliminar esta fila:

var parent = new Data.Models.JohnsParentTable() { Id = 1 };
db.JohnsParentTable.Attach(parent);
db.JohnsParentTable.Remove(parent);

db.SaveChanges();

Entity framework intenta ejecutar lo siguiente:

DELETE [dbo].[JohnsParentTable]
WHERE ([Id] = @0)
-- @0: '1' (Type = Int32)
-- Executing at 1/23/2014 10:34:01 AM -06:00
-- Failed in 103 ms with error: The DELETE statement conflicted with the REFERENCE constraint "FK_JohnsChildTable_JohnsParentTable". The conflict occurred in database "mydatabase", table "dbo.JohnsChildTable", column 'JohnsParentTableId'.
The statement has been terminated.

Entonces, mi pregunta es ¿qué es exactamente lo que me falta para garantizar que Entity Framework sepa que debe eliminar las filas de 'JohnsChildTable' antes de eliminar el padre?

Respuesta aceptada

Depende de si desea que Entity Framework elimine los elementos secundarios o si desea que la base de datos se ocupe de ello.

Si desea que EF genere una declaración de eliminación para todos los hijos y los ejecute antes de eliminar el padre, primero debe cargar todos los hijos en la memoria.

Por lo tanto, no puede simplemente crear una entidad "ficticia" con solo la clave rellenada, y esperar que se eliminen los secundarios.

Para que eso funcione, debes dejar que la base de datos maneje la eliminación.

Sin embargo, la clave externa en la tabla secundaria tendría que tener habilitadas las eliminaciones en cascada. Si ese es el caso, Entity Framework simplemente crea una declaración de eliminación para el padre, y la base de datos sabe que también debe eliminar los hijos.

Entity Framework crea una clave externa con las eliminaciones en cascada habilitadas de forma predeterminada, si la relación es necesaria, como en su caso. (La clave externa no puede ser nula).

Si ha creado la base de datos usted mismo, debe recordar habilitarla.


Respuesta popular

Creo que es mejor anular el método OnModelCreating y agregar este código.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
   modelBuilder.Entity<JohnsChildTable>()
               .HasRequired(t=>t.JohnsParentTable)
               .WithMany(t=>t.JohnsChildTables)
               .HasForeignKey(d=>d.JohnsParentTableId)
               .WillCascadeOnDelete(true);

            base.OnModelCreating(modelBuilder);
}

He establecido en verdadero WillCascadeOnDelete (verdadero)



Related

Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué