The DELETE statement conflicted with the SAME TABLE REFERENCE constraint with Entity Framework

前端 未结 3 1152
梦如初夏
梦如初夏 2021-01-01 18:15

I have a table with a self reference where the ParentId is an FK to the ID (PK).
Using EF (code-first), I\'ve set up my relationship as follows:

this.Ha         


        
3条回答
  •  长情又很酷
    2021-01-01 18:58

    Deleting parent and child like the following does work for me. The children are deleted before the parent and it is a single database roundtrip (one call to SaveChanges) with of course three DELETE statements in a single transaction:

    using (var ctx = new MyContext())
    {
        var parent = ctx.MyEntities.Include(e => e.Children).FirstOrDefault();
    
        foreach (var child in parent.Children.ToList())
            ctx.MyEntities.Remove(child);
    
        ctx.MyEntities.Remove(parent);
    
        ctx.SaveChanges();
    }
    

    (Using ToList() is necessary here because calling Remove for the children also removes from the parent's Children collection. Without using ToList a runtime exception would be thrown that the collection the foreach loop is iterating over has been modified.)

    The order in which Remove is called for children and parent doesn't matter. This works as well:

    using (var ctx = new MyContext())
    {
        var parent = ctx.MyEntities.Include(e => e.Children).FirstOrDefault();
    
        var children = parent.Children.ToList();
    
        ctx.MyEntities.Remove(parent);
    
        foreach (var child in children)
            ctx.MyEntities.Remove(child);
    
        ctx.SaveChanges();
    }
    

    EF sorts the DELETE statements in the correct order in both cases.

    Full test program (EF 5 / .NET 4.5 / SQL Server):

    using System.Collections.Generic;
    using System.Data.Entity;
    using System.Linq;
    
    namespace EFSelfReference
    {
        public class MyEntity
        {
            public int Id { get; set; }
            public string Name { get; set; }
    
            public int? ParentId { get; set; }
            public MyEntity Parent { get; set; }
    
            public ICollection Children { get; set; }
        }
    
        public class MyContext : DbContext
        {
            public DbSet MyEntities { get; set; }
    
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder.Entity()
                    .HasOptional(e => e.Parent)
                    .WithMany(e => e.Children)
                    .HasForeignKey(e => e.ParentId);
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Database.SetInitializer(
                    new DropCreateDatabaseAlways());
                using (var ctx = new MyContext())
                {
                    ctx.Database.Initialize(false);
    
                    var parent = new MyEntity { Name = "Parent",
                        Children = new List() };
    
                    parent.Children.Add(new MyEntity { Name = "Child 1" });
                    parent.Children.Add(new MyEntity { Name = "Child 2" });
    
                    ctx.MyEntities.Add(parent);
    
                    ctx.SaveChanges();
                }
    
                using (var ctx = new MyContext())
                {
                    var parent = ctx.MyEntities.Include(e => e.Children)
                        .FirstOrDefault();
    
                    foreach (var child in parent.Children.ToList())
                        ctx.MyEntities.Remove(child);
    
                    ctx.MyEntities.Remove(parent);
    
                    ctx.SaveChanges();
                }
            }
        }
    }
    

    Screenshot after the first using block with current content in DB table before the entities are deleted:

    screen 1

    Screenshot from SQL profiler after the last SaveChanges:

    screen 2

    I.e. Child 1 (Id = 2) and Child 2 (Id = 3) are deleted before Parent (Id = 1).

提交回复
热议问题