Entity Framework 6: Code First Cascade delete

c# entity-framework entity-framework-6 sql

Domanda

Quindi ci sono diverse domande simili su questo, ma sto ancora avendo problemi a determinare cosa esattamente mi manca nel mio scenario semplificato.

Diciamo che ho le seguenti tabelle, abilmente intitolate a me stesso:

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

Con le classi risultanti sembra così

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

Nel database ho una riga nella tabella padre con un ID di 1 insieme a due righe nella tabella figlio legate a questo genitore. Se faccio questo:

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

L'oggetto è popolato correttamente. Tuttavia, se provo a cancellare questa riga:

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

db.SaveChanges();

Il framework Entity tenta di eseguire quanto segue:

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.

La mia domanda quindi è cosa mi manca esattamente per garantire che Entity Framework sappia che deve eliminare le righe "JohnsChildTable" prima di eliminare il genitore?

Risposta accettata

Dipende se si desidera che Entity Framework elimini i figli o che il database si occupi di esso.

Se vuoi che EF generi un'istruzione delete per tutti i bambini ed esegua quelli prima di eliminare il genitore, devi prima caricare tutti i bambini in memoria.

Quindi non puoi semplicemente creare un'entità "fittizia" con solo la chiave popolata e aspettarti che i bambini vengano cancellati.

Affinché funzioni, devi lasciare che il database gestisca la cancellazione.

La chiave esterna sulla tabella figlio dovrebbe tuttavia abilitare le eliminazioni a cascata. In questo caso, Entity Framework crea semplicemente un'istruzione delete per il genitore e il database sa anche di eliminare i figli.

Entity Framework crea una chiave esterna con eliminazioni a cascata abilitate di default, se la relazione è richiesta, come nel tuo caso. (La chiave esterna non può essere nulla).

Se hai creato il database da solo, devi ricordarti di abilitarlo.


Risposta popolare

Penso che sia meglio ignorare il metodo OnModelCreating e aggiungere questo codice.

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

Ho impostato su true WillCascadeOnDelete (true)



Related

Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché