EF 4.1 CodeFirst - Single Cascade Delete

I am trying to remove a child from the navigation property of a parent collection. There is a one-to-many relationship established by b / t parent and child. As soon as I remove the child, I want the database to remove the link. a child record from the database, not orphans of that record by invalidating the foreign key.

Is there a way to do this without explicitly removing the child via the child DbSet in the DBC text?

I've seen other posts related to this topic, but I thought I'd refactor the code to a simpler test:

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

      

+3


source to share


2 answers


Is there a way to do this without explicitly deleting the child via the child DbSet in the DBC text?

In your model: No, there is no other way. You should call:

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

_context.SaveChanges();

      



I recently found out that there is one exception that causes automatic deletion in the database when the child is removed from the parent collection. That the so-called Identity Relationship, which requires the foreign key property in the child to refer to the parent, must be part of the primary key of the (composite) child:

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

      

In this case, your code will indeed remove the child from the database. It is explained here (last section at the bottom): http://msdn.microsoft.com/en-us/library/ee373856.aspx

+5


source


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

      

Read more here: fooobar.com/questions/477330 / ...

EDIT: Why is less general



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

      

and you just call it

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

      

0


source







All Articles