删除与Entity Framework或Entity Framework Core的父子关系

时间:2016-09-30 11:18:56

标签: c# entity-framework entity-framework-core

我正在学习如何使用具有NHibernate背景的Entity Framework。 网上有很多教程,但我没有找到适合我具体案例的内容。 我不想删除类别记录,我只想删除关系!

我关注了poco's:

    public class TrainingCourse
    {
        [Key]
        public int CourseId { get; set; }
        public string CourseName { get; set; }
        public virtual ICollection<TrainingContent> Content { get; set; } = new List<TrainingContent>();
    }

    public class TrainingContent
    {
        [Key]
        public int ContentId { get; set; }
        public string ContentName { get; set; }
        public int? CategoryId { get; set; }
        [ForeignKey("CategoryId")]
        public Category Category { get; set; }
    }

    public class Category
    {
        public int CategoryId { get; set; }
        public string CategoryName { get; set; }
    }

在数据库中,我有以下数据&#39;插入

            var category = new Category { CategoryName = "Category 1" };
            var course = new TrainingCourse { CourseName = "Course 1" };
            context.TrainingCourses.Add(course);
            var content = new TrainingContent { ContentName = "Content 1", Category = category };
            context.TrainingContents.Add(content);
            course.Content.Add(content);

我现在想删除从TrainingContent到Category的关系:在数据库术语中,我想在表TrainingContent中将我的外键CategoryId设置为null。

在一个上下文中执行此操作时,它正常工作,保存后我的外键为NULL:

        using (var context = new ClientContext())
        {
            _course = context.TrainingCourses.Include(c => c.Content.Select(cat => cat.Category)).FirstOrDefault(n => n.CourseName == "Course 1");

            _course.Content.ToList()[0].ContentName = "Content 1 changed";
            _course.Content.ToList()[0].Category = null;

            context.SaveChanges();
        }

但在现实世界中,我们正在处理断开连接的实体。 我用以下代码模拟这个

        using (var context = new ClientContext())
        {
            _course = context.TrainingCourses.Include(c => c.Content.Select(cat => cat.Category)).FirstOrDefault(n => n.CourseName == "Course 1");
        }

        _course.Content.ToList()[0].ContentName = "Content 1 changed";
        _course.Content.ToList()[0].Category = null;

        using (var context = new ClientContext())
        {
            context.Entry(_course.Content.ToList()[0]).State = EntityState.Modified;
            context.SaveChanges();
        }

这不起作用,内容名称已更改,但我的外键仍然已定义。

如果我将navigation属性设置为NULL并将FK设置为​​NULL,则它正在运行:

        using (var context = new ClientContext())
        {
            _course = context.TrainingCourses.Include(c => c.Content.Select(cat => cat.Category)).FirstOrDefault(n => n.CourseName == "Course 1");
        }

        _course.Content.ToList()[0].ContentName = "Course 1 changed";
        _course.Content.ToList()[0].Category = null;
        _course.Content.ToList()[0].CategoryId = null;

        using (var context = new ClientContext())
        {
            context.Entry(_course.Content.ToList()[0]).State = EntityState.Modified;
            context.SaveChanges();
        }

为什么会这样?我是EF和EF Core的新手,所以也许有一个简单的解释? 我最初使用Entity Framework Core对此进行了测试,但行为相同。

1 个答案:

答案 0 :(得分:0)

您的问题是,如果导航属性为null,则EF不会检查参照完整性(这将在SQL端发生)。

然而,这在大多数情况下是必要的。您可能希望将内容附加到其他类别:

var content=context.Contents.FirstOrDefault();
content.CategoryId=2;
context.Entry(content).State=EntityState.Modified;
context.SaveChanges();

看起来很好,不是吗?但是,请注意,您从未实际加载(延迟或显式)相应的Category对象 - 它仍为null。

因此,在此示例中以及在您的示例中,代码本身看起来很好,但FK和导航属性不匹配。但是,由于导航属性没有是正确的(除非被上下文跟踪,因此它被加载并且EF知道它是正确的),EF将期望FK是正确的,因此保存这个值进入数据库。

请注意,在少数情况下不会发生此问题:第一个显然是EF能够在其ChangeTracker中跟踪类别 - 然后它会知道您实际上想要考虑类别导航属性,而不一定是FK。 第二个是当你的FK不属于你的对象时。从那时起,EF必须创建一个FK,但是你不能自己设置它。然后EF必须设置与导航属性对应的FK。 (请注意,此不会使用null。null表示未加载值值为什么(以及其他一些含义) )并且EF无法确定哪一个是正确的。

另外,请记住,在断开连接的场景中,EF会在附加该树的根时检查整个对象树的参照完整性 - 如果任何FK与导航属性不匹配,EF将抛出异常(由于上述原因,null除外)。 并且:当您在断开连接的场景中工作时,请注意当任何父条目处于已添加状态时,必须附加所有子对象(atm我不知道它们是否也必须被修改/新建),否则EF无法修复儿童对象的FK并抛出异常。