2012-03-16 3 views
3

J'essaie de supprimer une entité enfant d'une propriété de navigation de collection parent. Il y a une relation un-à-plusieurs mise en place par parent et par enfant. Une fois que j'ai supprimé l'enfant, je veux que la base de données supprime l'assoc. enregistrement enfant de la base de données plutôt que de rendre orphelin cet enregistrement en annulant la clé étrangère.EF 4.1 CodeFirst - Un-à-plusieurs en cascade Supprimer

Y at-il un moyen de le faire sans avoir à supprimer explicitement l'enfant via le DbSet enfant dans le DBContext?

J'ai vu d'autres commentaires à ce sujet, mais je pensais que je distiller le code jusqu'à un test plus simple:

using System.Collections.Generic; 
using System.Data.Entity; 
using System.Linq; 
using NUnit.Framework; 

namespace basic_tests 
{ 
[TestFixture] 
public class OneToManyTests 
{ 
    #region Setup/Teardown 

    [SetUp] 
    public void SetUp() 
    { 
     _context = new Context(); 
     Database.SetInitializer(new DataInitializer()); 
    } 

    #endregion 

    private Context _context; 

    [Test] 
    public void CanRemoveChildThroughParent() 
    { 
     /** 

     this throws : "System.Data.Entity.Infrastructure.DbUpdateException : An error occurred while saving entities that do not expose foreign key properties for    their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of    exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for     
     details.System.Data.UpdateException : A relationship from the 'Child_MyParent' AssociationSet is in the 'Deleted' state. Given multiplicity constraints, a 
     corresponding 'Child_MyParent_Source' must also in the 'Deleted' state. 

     **/ 

     var parent = _context.Parents.FirstOrDefault(); 
     var firstChild = parent.MyChildren.FirstOrDefault(); 
     parent.MyChildren.Remove(firstChild); 

     _context.SaveChanges(); 

     var parentRefresh = new Context().Parents.FirstOrDefault(); 
     Assert.AreEqual(1, parentRefresh.MyChildren.Count); 

     var childrenCount = new Context().Children.Count(); 
     Assert.AreEqual(1, childrenCount); 
    } 
} 

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

public class Child 
{ 
    public virtual int Id { get; set; } 
    public virtual string Name { get; set; } 
    public virtual Parent MyParent { get; set; } 
} 

public class Context : DbContext 
{ 
    public DbSet<Parent> Parents { get; set; } 
    public DbSet<Child> Children { get; set; } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Entity<Child>() 
      .HasRequired(c => c.MyParent) 
      .WithMany(p => p.MyChildren) 
      .WillCascadeOnDelete(true); 
    } 
} 

public class DataInitializer : DropCreateDatabaseAlways<Context> 
{ 
    protected override void Seed(Context context) 
    { 
     for (var i = 0; i < 2; i++) 
     { 
      context.Children.Add(new Child 
            { 
             Name = "child" + i 
            }); 
     } 

     var parent = new Parent { Name = "parent", MyChildren = context.Children.Local.ToList() }; 

     context.Parents.Add(parent); 

     base.Seed(context); 
    } 
} 
} 
+0

Vous avez des mots-clés "virtuels" inutiles dans ce code. –

+1

Euh, doux, ça résout définitivement mon problème! – drogon

Répondre

5

Est-il possible de le faire sans avoir à supprimer explicitement l'enfant par l'enfant DbSet dans le DBContext?

Dans votre modèle: Non, il n'y a pas d'autre solution. Vous devez appeler:

var parent = _context.Parents.FirstOrDefault(); 
var firstChild = parent.MyChildren.FirstOrDefault(); 
_context.Children.Remove(firstChild); 

_context.SaveChanges(); 

J'ai récemment appris qu'il ya une exception qui provoque une supprimer automatique dans la base de données lorsque vous retirez l'enfant de la collection parent. C'est la soi-disant L'identification relation qui exige que la propriété clé étrangère chez l'enfant se référant à la société mère doit faire partie de la (composite) clé primaire de l'enfant:

public class Child 
{ 
    [Key, Column(Order = 0)] 
    public virtual int Id { get; set; } 
    [Key, ForeignKey("MyParent"), Column(Order = 1)] 
    public virtual int MyParentId { get; set; } 

    public virtual string Name { get; set; } 
    public virtual Parent MyParent { get; set; } 
} 

Dans ce cas, votre code serait en effet supprimer l'enfant de la base de données. Il est expliqué ici (dernière section en bas): http://msdn.microsoft.com/en-us/library/ee373856.aspx

0
var parent = context.Parents.Include(p=>p.Children).Where(p=>p.ParentId == 1); 

    foreach(Children child in parent.Children.ToList()) 
    { 
     context.Entry(child).State = EntityState.Deleted; 
    } 
    context.Entry(parent).State = EntityState.Deleted; 
    context.SaveChanges(); 

Pour plus d'informations rendez-vous ici: https://stackoverflow.com/a/9571108/1241400

EDIT: Pourquoi moins générique

public void DeleteMany<E>(IQueryable<E> entitiesToDelete) where E : class 
     { 
      foreach (var entity in entitiesToDelete) 
      { 
       DataContext.Entry(entity).State = System.Data.EntityState.Deleted; 
      } 
     } 

et vous aurez juste l'appeler

var childrenToDelete = someRepository.FindChildrenByParentId<Children>(ParentId); 
someRepository.DeleteMany<Children>(childrenToDelete); 
+0

Super, merci. C'est un bummer que vous ne pouvez pas simplement enlever l'enfant. Il semble que ce soit plus cohérent avec une suppression en cascade. Et maintenant votre soit votre classe doit être au courant des espaces de noms EF, ou vous devez rendre votre couche de datalayer moins générique .... – drogon

+0

a ajouté un exemple générique –

Questions connexes