2013-03-05 5 views
30

EntityFramework der Dokumentation wird zunichte zu machen, dass das folgende Verhalten möglich ist:EntityFramewok: So konfigurieren Cascade-Delete Fremdschlüssel

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.

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

Allerdings kann ich ein solches Verhalten nicht erreichen.

Ich habe folgende Entitäten definiert mit Code-first:

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

Hier ist die Fluent API Mapping-Konfiguration:

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

Fremdschlüssel NULL-Werte zulässt, Navigationseigenschaft als Optional abgebildet wird, so Ich erwarte, dass die Kaskadenlöschung wie beschrieben als MSDN funktioniert - dh die MasterIDs aller untergeordneten Elemente aufheben und dann das Master-Objekt löschen.

Aber wenn ich versuche, um tatsächlich zu löschen, erhalte ich die Fehler FK-Verletzung:

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

auf Savechanges() wirft die folgenden:

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. 

Bin ich etwas falsch zu machen oder ich habe missverstanden was die MSDN sagt?

Antwort

42

Es funktioniert zwar wie beschrieben, aber der Artikel auf MSDN fehlt zu betonen, dass es funktioniert nur, wenn die untergeordneten Elemente in den Kontext sowie nicht nur die übergeordnete Entität geladen werden. Anstatt also Find der Verwendung (die nur die Eltern lädt) Sie müssen eifrig Laden verwenden mit Include (oder irgendeine andere Art und Weise, die Kinder in den Kontext zu laden):

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

Diese den Master aus der Datenbank löschen wird, Setzen Sie alle Fremdschlüssel in den Entitäten Child auf null und schreiben Sie UPDATE-Anweisungen für die untergeordneten Elemente in die Datenbank.

+7

der fette Text speicherte mich. Lazy Loading ist großartig, aber stellen Sie sicher, dass die untergeordneten Objekte vor dem Löschen geladen werden ... – amaters

+0

Ich versuche dieses Beispiel. Habe aber einen Fehler in "include" bekommen. es sagt "kann lamba Ausdruck nicht in Typ" string "konvertieren, weil es kein Delegattyp ist .... Was fehlt mir? Vielen Dank – Diego

0

Nach der folgenden @ Slaumas große Antwort bekam ich immer noch denselben Fehler wie OP.

Also seien Sie nicht so naiv wie ich und denken Sie, dass die Beispiele unten mit dem gleichen Ergebnis enden werden.

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; 

Erste Last der Kinder in einem Kontext vor Einstellung Entitätsstatus zu löschen (wenn Sie es auf diese Weise tun).

Verwandte Themen