刪除與Entity Framework或Entity Framework Core的父子關係

c# entity-framework entity-framework-core

我正在學習如何使用具有NHibernate背景的Entity Framework。網上有很多教程,但我沒有找到適合我具體案例的內容。我不想刪除Category記錄,我只想刪除關係!

我跟隨poco的:

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

在數據庫中,我插入了“數據”

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

這不起作用,內容名稱已更改,但仍定義了我的外鍵。

如果我將導航屬性設置為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對此進行了測試,但行為相同。

一般承認的答案

您的問題是,如果導航屬性為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並拋出異常。



Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因