2013-05-20 12 views
39

Cóż, mam jeden-do-wielu pokrewnych modeluJak usunąć element potomny do wielu powiązanych rekordów w pierwszej bazie danych kodu 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; } 
} 

Co chcę zrobić, to jasne Parent.Children i wyjąć dziecko Podmioty powiązane z bazą danych. Ja już próbowałem:

Database klasa kontekst:

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

to działa dobrze, ale nadal mam zbędne rekordy w bazie danych z Parent_Id = null pól Kiedy zrobić

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

w moim klasa repozytorium. Również takie samo zachowanie jest, gdy robię:

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

z dodatkowym Parent własności Child klasa

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

lub gdy robię

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

z dodatkową właściwość parent_id w Child klasy

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

Jak więc poprawnie skonfigurować usuwanie kaskadowe? Lub jak powinienem usunąć te elementy podrzędne? Zakładam, że to zwykłe zadanie, ale po prostu coś mi brakuje.

+0

Czy możesz dodać więcej kodu? Może pełna zawartość twojego 'OnModelCreating()'? Kiedy kopiuję i wklejaję twoje encje i twoją pierwszą próbę odwzorowania (i ustawiasz "Id" jako kluczową właściwość dla obu encji), usunięte są dla mnie kaskadowo. –

Odpowiedz

37

Kaskadowe usuwanie nie ma wpływu tutaj, ponieważ nie usunąć parent ale po prostu zadzwoń InsertOrUpdate. Prawidłowa procedura jest usunięcie dzieci jedno po drugim, tak jak na przykład:

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

Na pewno z kasowaniem usunąć, nie trzeba usuwać dzieci indywidualnie? –

+5

@kirsteng: Kaskadowe usuwanie oznacza, że ​​dzieci są usuwane ** jeśli ** usuniesz rodzica.Ale w tym pytaniu żaden rodzic nie jest usuwany. Tak więc usuwanie kaskadowe nie ma zastosowania do tego scenariusza. – Slauma

+8

zamiast przepowiadanie wszystkich obiektów "potomnych", po prostu powiedz 'context.Children.RemoveRange (parent.Children.ToArray())' w ten sposób DbContext nie musi wykonywać tak wiele sprawdzeń pracy za każdym razem, gdy wywołujemy Remove. To może nie być duży problem z wydajnością usuwania, ale zauważyłem ogromną różnicę przy dodawaniu elementów po jednym podczas testowania dodawania 100k rekordów za pomocą 'context.Children.Add (child)' w pętli for. – matrixugly

3

Spróbuj zmienić

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

ponieważ wirtualny jest potrzebne, aby dostać leniwy załadunku. jak wyjaśniono here

Myślę, że parent.Children.clear isnt działa, ponieważ dzieci nie zostały załadowane

86

W EF6 szybszy sposób zrobić operacja jest ...

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

Jest to szybszy sposób kodowania, ale pod względem wydajności, czy jest naprawdę szybszy? Jeśli chcę usunąć wszystkie podrzędne elementy podrzędne z 'parent.Id == 10', a moja klasa' Child' ma 100 pól i jeszcze nie została załadowana do pamięci, użycie polecenia 'RemoveRange' nie jest wydajne, ponieważ program będzie miał aby załadować domyślnie 'parent.Children' ze wszystkimi 100 polami. – VCD

+0

@ VCD jest szybszy niż wywołanie context.Children.Remove (child) dla każdego dziecka, ale nie tak szybki jak uruchomienie sql "DELETE FROM Children" gdzie parentId = @ parentId " –

+0

Jak byś poszła na znalezienie rodzica, jeśli jedyne informacje Czy to dziecko jest Id? – guyfromfargo

0

Jeśli obiekt jest samoreferencyjne, można usunąć zarówno wiele-do-wielu i jeden-do-wielu dzieci za pomocą poniższej metody. Pamiętaj tylko, aby wywołać funkcję db.SaveChanges(). :)

[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); 
    } 
} 
Powiązane problemy