EF Core 3.1.7 คำอธิบายประกอบข้อมูลสำหรับความสัมพันธ์แบบ 1: 1 หลายรายการในตาราง

Aug 17 2020

ฉันมีปัญหาในการหาคำอธิบายประกอบข้อมูลเพื่อแมปความสัมพันธ์แบบ 1: 1 มากกว่าหนึ่งความสัมพันธ์เพื่อให้ EF Core 3.11.7 เข้าใจและสร้างการย้ายข้อมูลได้

ฉันมีตารางบุคคลและตารางบันทึกย่อ มีความสัมพันธ์ 0: M Notes ในตัวบุคคล บันทึกบุคคลสามารถมีบันทึกได้ตั้งแต่ 0 รายการขึ้นไป

ในตารางบันทึกย่อคือเขตข้อมูล CreatedBy ซึ่งเป็นบุคคล มันยังมีฟิลด์ LastEditedBy ซึ่งเป็นคนด้วย EF ยังคงทิ้งระเบิดในการสร้างความสัมพันธ์สำหรับ Note.CreatedBy หากนี่ไม่ใช่ EF ทั้งสองฟิลด์จะถูก ints กับ PersonID ของเร็กคอร์ดบุคคลที่เหมาะสม อธิบายสิ่งนี้กับ EF Core ได้อย่างไรโดยเฉพาะอย่างยิ่งคำอธิบายประกอบข้อมูล

เมื่อฉันพยายามสร้างการย้ายข้อมูลล้มเหลวและแจ้งว่า: System.InvalidOperationException: ไม่สามารถระบุความสัมพันธ์ที่แสดงโดยคุณสมบัติการนำทาง 'Note.CreatedBy' ประเภท 'บุคคล' กำหนดค่าความสัมพันธ์ด้วยตนเองหรือละเว้นคุณสมบัตินี้โดยใช้แอตทริบิวต์ "[NotMapped]" หรือโดยใช้ "EntityTypeBuilder.Ignore" ใน "OnModelCreating"

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Linq;
    using System.Threading.Tasks;

namespace VetReg.Domain.Model
{
    public class Family
    {
        public int FamilyID { get; set; } = -1;
        public string FamilyName { get; set; }
        public List<Pet> Pets { get; set; } = new List<Pet>();
        public List<PersonFamily> People { get; set; }
        public int AddressID { get; set; } = -1;
        public List<Note> Notes { get; set; }
    }

    public class Person
    {
        public int PersonID { get; set; }
        public string LastName { get; set; }
        public string FirstName { get; set; }
        public DateTime? Birthdate { get; set; }
        public string Password { get; set; }
        public List<PersonFamily> Families { get; set; }
        public List<Note> Notes { get; set; }
    } // class People

    public class Note
    {
        public int NoteID { get; set; }

        public int CreatedByID { get; set; }

        [ForeignKey("CreatedByID")]
        public Person CreatedBy { get; set; }
        public DateTime DateCreated { get; set; }

        public int LastEditByID { get; set; }

        [ForeignKey("LastEditByID")]
        public Person LastEditBy { get; set; }
        public DateTime? LastEditDate { get; set; }
        public string NoteText { get; set; }
    }

    public class PersonFamily
    {
        public int PersonID { get; set; }
        public int FamilyID { get; set; }
        public Person Person { get; set; }
        public Family Family { get; set; }
    }

}

คำตอบ

IvanStoev Aug 17 2020 at 15:11

คำถามคือ (และนี่คือสิ่งที่ทำให้ EF ไม่สามารถกำหนดความสัมพันธ์โดยอัตโนมัติได้) ความสัมพันธ์ระหว่างPerson.NotesและNote.CreatedBy/ Note.LastEditBy- ไม่มีน่าจะเป็นอย่างไร? คุณได้กล่าวว่ามีความสัมพันธ์ 0: M ระหว่างPersonและNoteแต่โปรดทราบว่ามีความสัมพันธ์แบบหนึ่งต่อกลุ่ม 3 ที่อาจมีอยู่นั่นคือบันทึกที่เกี่ยวข้องกับบุคคลบันทึกย่อที่สร้างขึ้นโดยบุคคลและบันทึกย่อที่แก้ไขโดยบุคคลซึ่งอาจนำไปสู่ ​​3 FK ถึง บุคคลในหมายเหตุ

โปรดทราบว่าไม่จำเป็นต้องใช้คุณสมบัติการนำทางใด ๆ แต่จะต้องจับคู่เมื่อมี

สมมติว่าคุณต้องการ 3 ความสัมพันธ์กล่าวคือไม่มีความสัมพันธ์ระหว่างNote.CreatedBy/ Note.LastEditByและPerson.Notesคุณต้องบอก EF ว่าNote.CreatedByและNote.LastEditByไม่มีคุณสมบัติการนำทางที่สอดคล้องกัน (aka inverse) ในPerson. สิ่งนี้ไม่สามารถทำได้ด้วยคำอธิบายประกอบข้อมูล คำอธิบายประกอบข้อมูลที่มีอยู่เดียวสำหรับวัตถุประสงค์[InverseProperty(...)]นั้นไม่ยอมรับชื่อสตริงว่าง / ว่างดังนั้นจึงไม่สามารถใช้กับสิ่งที่จำเป็นที่นี่ได้

นอกจากนี้ยังมีปัญหาอื่นที่คุณจะพบหลังจากแก้ไขปัญหาปัจจุบันซึ่งไม่สามารถแก้ไขได้ด้วยคำอธิบายประกอบข้อมูล เนื่องจากคุณจำเป็นต้องมีหลาย ๆ (เช่นน้ำตกลบโดยค่าเริ่มต้น) ความสัมพันธ์จากPersonการNoteจะสร้างที่มีชื่อเสียง "รอบหรือเส้นทางน้ำตกหลาย" ปัญหากับ SqlServer และต้องปิดที่หนึ่งที่น้อยที่สุดของน้ำตกลบ

ด้วยเหตุนี้โมเดลที่เป็นปัญหาจึงต้องการการกำหนดค่าที่คล่องแคล่วน้อยที่สุดดังต่อไปนี้:

modelBuilder.Entity<Note>()
    .HasOne(e => e.CreatedBy)
    .WithMany()
    .OnDelete(DeleteBehavior.Restrict);

modelBuilder.Entity<Note>()
    .HasOne(e => e.LastEditBy)
    .WithMany()
    .OnDelete(DeleteBehavior.Restrict);

สิ่งสำคัญสำหรับปัญหาเดิมคือคู่HasOne/ WithManyเมื่อคุณทำเช่นนั้น EF Core จะแมปPerson.Notesคอลเลคชันที่ไม่ได้แมปกับความสัมพันธ์ทางเลือกที่สามโดยอัตโนมัติโดยไม่มีคุณสมบัติการนำทางผกผันและคุณสมบัติเงา FP (และคอลัมน์) ที่เรียกว่า "PersonId" นั่นคือค่าที่เทียบเท่ากับ

modelBuilder.Entity<Person>()
    .HasMany(e => e.Notes)
    .WithOne()
    .HasForeignKey("PersonId");

Regarding the second issue with multiple cascade paths, instead of Restrict you can use any non cascading option or the newer ClientCascade. And it could be for just one of the relationships, as soon as it breaks the "cascade path" (apparently you can't break the cycle because it is demanded by the model).