Problems using TPT (Table Per Type) in EF 4.2 and deletion of parent objects

前端 未结 1 1186
青春惊慌失措
青春惊慌失措 2020-12-10 04:48

From what I understand on several posts the TPT architecure, with EF, does not create the necessary ON DELETE CASCADE when using a shared primary key.... It was also said th

相关标签:
1条回答
  • 2020-12-10 05:34

    I have reproduced the problem with a little bit simpler example:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Data.Entity;
    
    namespace EFTPT
    {
        public class Parent
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public ICollection<BasePage> Pages { get; set; }
        }
    
        public abstract class BasePage
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public Parent Parent { get; set; }
        }
    
        public class DerivedPage : BasePage
        {
            public string DerivedName { get; set; }
        }
    
        public class MyContext : DbContext
        {
            public DbSet<Parent> Parents { get; set; }
            public DbSet<BasePage> BasePages { get; set; }
    
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder.Entity<Parent>()
                    .HasMany(p => p.Pages)
                    .WithRequired(p => p.Parent);  // creates casc. delete in DB
    
                modelBuilder.Entity<BasePage>()
                    .ToTable("BasePages");
    
                modelBuilder.Entity<DerivedPage>()
                    .ToTable("DerivedPages");
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                using (var ctx = new MyContext())
                {
                    var parent = new Parent { Pages = new List<BasePage>() };
                    var derivedPage = new DerivedPage();
    
                    parent.Pages.Add(derivedPage);
    
                    ctx.Parents.Add(parent);
                    ctx.SaveChanges();
                }
    
                using (var ctx = new MyContext())
                {
                    var parent = ctx.Parents.FirstOrDefault();
                    ctx.Parents.Remove(parent);
                    ctx.SaveChanges();  // exception here
                }
            }
        }
    }
    

    This gives the same exception that you had too. Only solutions seem to be:

    • Either setup cascading delete for the TPT constraint in the DB manually, as you already tested (or put an appropriate SQL command into the Seed method).
    • Or load the entites which are involved in the TPT inheritance into memory. In my example code:

      var parent = ctx.Parents.Include(p => p.Pages).FirstOrDefault();
      

      When the entities are loaded into the context, EF creates actually two DELETE statements - one for the base table and one for the derived table. In your case, this is a terrible solution because you had to load a much more complex object graph before you can get the TPT entities.

    Even more problematic is if Parent has an ICollection<DerivedPage> (and the inverse Parent property is in DerivedPage then):

    public class Parent
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public ICollection<DerivedPage> Pages { get; set; }
    }
    
    public abstract class BasePage
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    
    public class DerivedPage : BasePage
    {
        public string DerivedName { get; set; }
        public Parent Parent { get; set; }
    }
    

    The example code wouldn't throw an exception but instead delete the row from the derived table but not from the base table, leaving a phantom row which cannot represent an entity anymore because BasePage is abstract. This problem is not solvable by a cascading delete but you were actually forced to load the collection into the context before you can delete the parent to avoid such a nonsense in the database.

    A similar question and analysis was here: http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/3c27d761-4d0a-4704-85f3-8566fa37d14e/

    0 讨论(0)
提交回复
热议问题