EF Core 3.1.7 คำอธิบายประกอบข้อมูลสำหรับความสัมพันธ์แบบ 1: 1 หลายรายการในตาราง
ฉันมีปัญหาในการหาคำอธิบายประกอบข้อมูลเพื่อแมปความสัมพันธ์แบบ 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; }
}
}
คำตอบ
คำถามคือ (และนี่คือสิ่งที่ทำให้ 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).