2013-05-20 13 views
39

Beh, ho uno-a-molti modelli relativi:Come rimuovere il figlio da uno a molti record correlati nel primo database del codice EF?

public class Parent 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public ICollection<Child> Children { get; set; } 
} 

public class Child 
{ 
    public int Id { get; set; } 
    public string ChildName { get; set; } 
} 

Quello che voglio fare è chiaro Parent.Children e rimuovere entità figlio correlati da database. Ho già provato:

Database classe del contesto:

modelBuilder.Entity<Parent>() 
      .HasMany(p => p.Children) 
      .WithOptional() 
      .WillCascadeOnDelete(true); 

Questo funziona bene, ma ho ancora record ridondanti nel database con Parent_Id = null campi quando faccio

parent.Children.Clear(); 
repository.InsertOrUpdate(parent); 

nel mio classe di repository. Anche lo stesso comportamento è quando faccio:

modelBuilder.Entity<Parent>() 
      .HasMany(pr => pr.Children) 
      .WithOptional(ri => ri.Parent) 
      .WillCascadeOnDelete(true); 

con l'aggiunta di Parent proprietà in Child classe

public class Child 
{ 
    ... 
    public Parent Parent { get; set; } 
    ... 
} 

o quando faccio

modelBuilder.Entity<Child>() 
      .HasOptional(p => p.Parent) 
      .WithMany(p => p.Children) 
      .HasForeignKey(p => p.Parent_Id) 
      .WillCascadeOnDelete(true); 

con la proprietà parent_id supplementare in Child classe

public class Child 
{ 
    ... 
    public int Parent_Id { get; set; } 
    ... 
} 

Quindi, come posso configurare correttamente l'eliminazione a cascata? O come dovrei rimuovere quelle entità infantili? Presumo che questo sia un compito casuale ma mi manca qualcosa.

+0

Puoi pubblicare altro codice? Forse il contenuto completo del tuo 'OnModelCreating()'? Quando copio e incolla le tue entità e il tuo primo tentativo di mappatura (e setto 'Id' come proprietà chiave per entrambe le entità), le eliminazioni sono a cascata per me. –

risposta

37

Cascading cancellare non ha alcun effetto qui perché non si elimina il parent ma basta chiamare InsertOrUpdate. La procedura corretta è quella di eliminare i bambini uno per uno, in questo modo, ad esempio:

using (var context = new MyContext()) 
{ 
    var parent = context.Parents.Include(p => p.Children) 
     .SingleOrDefault(p => p.Id == parentId); 

    foreach (var child in parent.Children.ToList()) 
     context.Children.Remove(child); 

    context.SaveChanges(); 
} 
+0

Sicuramente con eliminazione a cascata su di voi non è necessario eliminare i bambini individualmente? –

+5

@kirsteng: Eliminazione in cascata significa che i bambini vengono cancellati ** se ** si elimina il genitore.Ma in questa domanda nessun genitore viene cancellato. Quindi, l'eliminazione a cascata non si applica a questo scenario. – Slauma

+8

invece di forzare tutti gli oggetti "figlio", basta dire "context.Children.RemoveRange (parent.Children.ToArray())" in questo modo il DbContext non deve eseguire il controllo di tanto lavoro ogni volta che si chiama Rimuovi. Questo potrebbe non essere un grosso problema di prestazioni per le eliminazioni, ma ho notato un'enorme differenza quando aggiungo gli articoli uno alla volta quando ho provato ad aggiungere 100k record usando 'context.Children.Add (child)' in un ciclo for. – matrixugly

3

provare a cambiare il

public virtual ICollection<Child> Children { get; set; } 

perché è necessario virtuale per ottenere lazy loading. come spiegato here

Credo che la vostra parent.Children.clear isnt lavorando perché i bambini non sono stati caricati

86

In EF6 un modo più veloce per fare l'operazione è ...

context.Children.RemoveRange(parent.Children) 
+0

È un modo più rapido per codificare, ma in termini di prestazioni, è davvero più veloce? Se voglio eliminare tutti i bambini sotto genitore con 'parent.Id == 10', e la mia classe' Child' ha 100 campi e non sono ancora stati caricati in memoria, sembra inefficiente usare 'RemoveRange' perché il programma avrà per caricare 'parent.Children' con tutti i 100 campi per impostazione predefinita. – VCD

+0

@VCD è più veloce di chiamare context.Children.Remove (figlio) per ogni figlio ma non veloce come eseguire sql "DELETE FROM Children where parentId = @ parentId" –

+0

Come andresti a trovare il genitore, se le uniche informazioni che hai è l'ID bambino? – guyfromfargo

0

Se l'oggetto è autoreferenziale, puoi eliminare sia i bambini molti a molti sia quelli uno-a-molti usando il metodo seguente. Ricordati di chiamare db.SaveChanges() in seguito :)

[HttpPost, ActionName("Delete")] 
[ValidateAntiForgeryToken] 
public ActionResult DeleteConfirmed(int id) 
{ 
    Object obj = this.db.Objects.Find(id); 
    this.DeleteObjectAndChildren(obj); 
    this.db.Objects.Remove(obj); 
    this.db.SaveChanges(); 
    return this.Json(new { success = true }); 
} 

/// <summary> 
/// This deletes an object and all children, but does not commit changes to the db. 
/// - MH @ 2016/08/15 14:42 
/// </summary> 
/// <param name="parent"> 
/// The object. 
/// </param> 
private void DeleteObjectAndChildren(Object parent) 
{ 
    // Deletes One-to-Many Children 
    if (parent.Things != null && parent.Things.Count > 0) 
    { 
     this.db.Things.RemoveRange(parent.Things); 
    } 

    // Deletes Self Referenced Children 
    if (parent.Children != null && parent.Children.Count > 0) 
    { 
     foreach (var child in parent.Children) 
     { 
      this.DeleteObjectAndChildren(child); 
     } 

     this.db.Objects.RemoveRange(parent.Children); 
    } 
} 
Problemi correlati