2013-03-05 2 views
30

documentazione del EntityFramework afferma che il comportamento seguente è possibile:EntityFramewok: Come configurare Cascade-delete per annullare chiavi esterne

If a foreign key on the dependent entity is nullable, Code First does not set cascade delete on the relationship, and when the principal is deleted the foreign key will be set to null.

(da http://msdn.microsoft.com/en-us/jj591620)

Tuttavia, non riesco a raggiungere un tale comportamento.

Ho le seguenti entità definite con il codice di-prima:

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

Ecco il perfetto configurazione della mappatura API:

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

Foreign Key è annullabile, proprietà di navigazione è mappata come optional, così Mi aspetto che l'eliminazione a cascata funzioni come descritto da MSDN, ovvero annullare tutti gli ID Master di tutti i bambini e quindi eliminare l'oggetto Master.

Ma quando in realtà si tenta di eliminare, ottengo l'errore di violazione di FK:

using (var dbContext = new TestContext()) 
     { 
      var master = dbContext.Set<TestMaster>().Find(1); 
      dbContext.Set<TestMaster>().Remove(master); 
      dbContext.SaveChanges(); 
     } 

On SaveChanges() getta la seguente:

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. 

sto facendo qualcosa di sbagliato o fatto io frainteso cosa dice l'MSDN?

risposta

42

Funziona come descritto ma l'articolo su MSDN manca di sottolineare che funziona solo se i bambini sono caricati nel contesto, non solo l'entità padre. Così, invece di usare Find (che carica solo il genitore) è necessario utilizzare eager loading con Include (o qualsiasi altro modo per caricare i bambini nel contesto):

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

Questo cancellerà il maestro dal database, impostare tutte le chiavi esterne nelle entità Child su null e scrivere istruzioni UPDATE per i figli nel database.

+7

il testo in grassetto mi ha salvato. Il caricamento lento è ottimo, ma assicurati che gli oggetti figlio siano caricati prima di eliminare ... – amaters

+0

Sto provando questo esempio. Ma ho un errore in "include". dice "impossibile convertire l'espressione di lamba in" string "perché non è un tipo delegato" .... Cosa mi manca? Grazie – Diego

0

Dopo aver seguito l'ottima risposta di @ Slauma, stavo ancora ricevendo lo stesso errore di OP.

Quindi non essere così ingenuo come me e pensa che gli esempi seguenti finiranno con lo stesso risultato.

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; 

primo carico i bambini in contesto prima impostazione dello stato soggetti ai cancellato (se si sta facendo in questo modo).