Entity Framework - คำอธิบายประกอบข้อมูล

DataAnnotations ใช้เพื่อกำหนดค่าคลาสซึ่งจะเน้นการกำหนดค่าที่จำเป็นโดยทั่วไป นอกจากนี้ DataAnnotations ยังเข้าใจโดยแอปพลิเคชัน. NET จำนวนมากเช่น ASP.NET MVC ซึ่งช่วยให้แอปพลิเคชันเหล่านี้ใช้ประโยชน์จากคำอธิบายประกอบเดียวกันสำหรับการตรวจสอบความถูกต้องฝั่งไคลเอ็นต์ แอตทริบิวต์ DataAnnotation แทนที่อนุสัญญา CodeFirst เริ่มต้น

System.ComponentModel.DataAnnotations รวมแอตทริบิวต์ต่อไปนี้ที่ส่งผลต่อความเป็นโมฆะหรือขนาดของคอลัมน์

  • Key
  • Timestamp
  • ConcurrencyCheck
  • Required
  • MinLength
  • MaxLength
  • StringLength

System.ComponentModel.DataAnnotations.Schema เนมสเปซประกอบด้วยแอ็ตทริบิวต์ต่อไปนี้ที่มีผลต่อสคีมาของฐานข้อมูล

  • Table
  • Column
  • Index
  • ForeignKey
  • NotMapped
  • InverseProperty

สำคัญ

Entity Framework อาศัยทุกเอนทิตีที่มีค่าคีย์ที่ใช้ในการติดตามเอนทิตี หนึ่งในอนุสัญญาที่ Code First ขึ้นอยู่กับว่ามันบอกเป็นนัยว่าคุณสมบัติใดเป็นกุญแจสำคัญในแต่ละคลาสของ Code First

  • Convention คือการมองหาคุณสมบัติชื่อ“ Id” หรือคุณสมบัติที่รวมชื่อคลาสและ“ Id” เช่น“ StudentId”

  • คุณสมบัติจะแมปกับคอลัมน์คีย์หลักในฐานข้อมูล

  • ชั้นเรียนนักเรียนหลักสูตรและการลงทะเบียนเป็นไปตามอนุสัญญานี้

ตอนนี้สมมติว่าคลาสนักเรียนใช้ชื่อ StdntID แทน ID เมื่อ Code First ไม่พบคุณสมบัติที่ตรงกับข้อตกลงนี้จะทำให้เกิดข้อยกเว้นเนื่องจากข้อกำหนดของ Entity Framework ที่คุณต้องมีคุณสมบัติหลัก คุณสามารถใช้คำอธิบายประกอบคีย์เพื่อระบุคุณสมบัติที่จะใช้เป็น EntityKey

ลองมาดูรหัสต่อไปนี้ของชั้นเรียนนักเรียนซึ่งมี StdntID แต่ไม่เป็นไปตามหลักการเริ่มต้นของ Code First เพื่อจัดการกับสิ่งนี้จึงมีการเพิ่มแอตทริบิวต์คีย์ซึ่งจะทำให้เป็นคีย์หลัก

public class Student {

   [Key]
   public int StdntID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

เมื่อคุณเรียกใช้แอปพลิเคชันของคุณและตรวจสอบฐานข้อมูลของคุณใน SQL Server Explorer คุณจะเห็นว่าขณะนี้คีย์หลักคือ StdntID ในตารางนักเรียน

Entity Framework ยังรองรับคีย์คอมโพสิต Composite keysยังเป็นคีย์หลักที่ประกอบด้วยคุณสมบัติมากกว่าหนึ่งรายการ ตัวอย่างเช่นคุณมีคลาส DrivingLicense ซึ่งมีคีย์หลักผสมระหว่าง LicenseNumber และ IssuingCountry

public class DrivingLicense {

   [Key, Column(Order = 1)]
   public int LicenseNumber { get; set; }
   [Key, Column(Order = 2)]
   public string IssuingCountry { get; set; }
   public DateTime Issued { get; set; }
   public DateTime Expires { get; set; }
}

เมื่อคุณมีคีย์คอมโพสิต Entity Framework ต้องการให้คุณกำหนดลำดับของคุณสมบัติคีย์ คุณสามารถทำได้โดยใช้คำอธิบายประกอบคอลัมน์เพื่อระบุคำสั่งซื้อ

การประทับเวลา

Code First จะปฏิบัติต่อคุณสมบัติ Timestamp เช่นเดียวกับคุณสมบัติ ConcurrencyCheck แต่จะทำให้แน่ใจด้วยว่าฟิลด์ฐานข้อมูลที่โค้ดสร้างขึ้นครั้งแรกนั้นไม่เป็นค่าว่าง

  • เป็นเรื่องปกติมากขึ้นที่จะใช้เขตข้อมูล rowversion หรือ timestamp สำหรับการตรวจสอบการทำงานพร้อมกัน

  • แทนที่จะใช้คำอธิบายประกอบ ConcurrencyCheck คุณสามารถใช้คำอธิบายประกอบ TimeStamp ที่เฉพาะเจาะจงมากขึ้นได้ตราบเท่าที่ชนิดของคุณสมบัติเป็นไบต์อาร์เรย์

  • คุณสามารถมีคุณสมบัติการประทับเวลาได้เพียงหนึ่งรายการในคลาสที่กำหนด

ลองดูตัวอย่างง่ายๆโดยการเพิ่มคุณสมบัติ TimeStamp ในคลาส Course -

public class Course {

   public int CourseID { get; set; }
   public string Title { get; set; }
   public int Credits { get; set; }
   [Timestamp]
   public byte[] TStamp { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

ดังที่คุณเห็นในตัวอย่างข้างต้นแอตทริบิวต์การประทับเวลาถูกนำไปใช้กับคุณสมบัติ Byte [] ของคลาสหลักสูตร ดังนั้น Code First จะสร้างคอลัมน์การประทับเวลาTStampในตารางหลักสูตร

การทำงานพร้อมกันตรวจสอบ

คำอธิบายประกอบ ConcurrencyCheck ช่วยให้คุณสามารถตั้งค่าสถานะคุณสมบัติอย่างน้อยหนึ่งรายการเพื่อใช้สำหรับการตรวจสอบพร้อมกันในฐานข้อมูลเมื่อผู้ใช้แก้ไขหรือลบเอนทิตี หากคุณเคยทำงานกับ EF Designer สิ่งนี้จะสอดคล้องกับการตั้งค่า ConcurrencyMode ของคุณสมบัติเป็นคงที่

ลองมาดูตัวอย่างง่ายๆของการทำงานของ ConcurrencyCheck โดยการเพิ่มเข้าไปในคุณสมบัติ Title ในคลาส Course

public class Course {

   public int CourseID { get; set; }
   [ConcurrencyCheck]
   public string Title { get; set; }
   public int Credits { get; set; }
   [Timestamp, DataType("timestamp")]
   public byte[] TimeStamp { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

ในชั้นเรียนหลักสูตรข้างต้นแอตทริบิวต์ ConcurrencyCheck ถูกนำไปใช้กับคุณสมบัติ Title ที่มีอยู่ ตอนนี้ Code First จะรวมคอลัมน์ Title ไว้ในคำสั่ง update เพื่อตรวจสอบการเกิดพร้อมกันในแง่ดีดังที่แสดงในโค้ดต่อไปนี้

exec sp_executesql N'UPDATE [dbo].[Courses]
   SET [Title] = @0
   WHERE (([CourseID] = @1) AND ([Title] = @2))
   ',N'@0 nvarchar(max) ,@1 int,@2 nvarchar(max) ',@0=N'Maths',@1=1,@2=N'Calculus'
go

คำอธิบายประกอบที่จำเป็น

คำอธิบายประกอบที่จำเป็นจะบอก EF ว่าจำเป็นต้องมีคุณสมบัติเฉพาะ มาดูคลาสนักเรียนต่อไปนี้ซึ่งไอดีที่ต้องการถูกเพิ่มเข้าไปในคุณสมบัติ FirstMidName แอตทริบิวต์ที่จำเป็นจะบังคับให้ EF ตรวจสอบให้แน่ใจว่าคุณสมบัตินั้นมีข้อมูลอยู่

public class Student {

   [Key]
   public int StdntID { get; set; }

   [Required]
   public string LastName { get; set; }

   [Required]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

ดังที่เห็นในตัวอย่างข้างต้นแอตทริบิวต์ที่จำเป็นถูกนำไปใช้กับ FirstMidName และ LastName ดังนั้น Code First จะสร้างคอลัมน์ NOT NULL FirstMidName และ LastName ในตาราง Students ดังที่แสดงในภาพต่อไปนี้

MaxLength

แอตทริบิวต์ MaxLength ช่วยให้คุณระบุการตรวจสอบคุณสมบัติเพิ่มเติม สามารถนำไปใช้กับคุณสมบัติประเภทสตริงหรืออาร์เรย์ของคลาสโดเมน EF Code First จะกำหนดขนาดของคอลัมน์ตามที่ระบุในแอตทริบิวต์ MaxLength

มาดูคลาสหลักสูตรต่อไปนี้ซึ่งใช้แอตทริบิวต์ MaxLength (24) กับคุณสมบัติ Title

public class Course {

   public int CourseID { get; set; }
   [ConcurrencyCheck]
   [MaxLength(24)]
   public string Title { get; set; }
   public int Credits { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

เมื่อคุณเรียกใช้แอปพลิเคชันข้างต้น Code First จะสร้างชื่อคอลัมน์ nvarchar (24) ในตาราง CourseId ดังที่แสดงในภาพต่อไปนี้

เมื่อผู้ใช้ตั้งค่า Title ซึ่งมีอักขระมากกว่า 24 ตัว EF จะโยน EntityValidationError

MinLength

แอตทริบิวต์ MinLength ยังช่วยให้คุณสามารถระบุการตรวจสอบคุณสมบัติเพิ่มเติมได้เช่นเดียวกับที่คุณทำกับ MaxLength นอกจากนี้ยังสามารถใช้แอตทริบิวต์ MinLength กับแอตทริบิวต์ MaxLength ดังที่แสดงในรหัสต่อไปนี้

public class Course {

   public int CourseID { get; set; }
   [ConcurrencyCheck]
   [MaxLength(24) , MinLength(5)]
   public string Title { get; set; }
   public int Credits { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

EF จะโยน EntityValidationError ถ้าคุณตั้งค่าคุณสมบัติ Title น้อยกว่าความยาวที่ระบุในแอตทริบิวต์ MinLength หรือมากกว่าความยาวที่ระบุในแอตทริบิวต์ MaxLength

StringLength

StringLength ยังช่วยให้คุณระบุการตรวจสอบคุณสมบัติเพิ่มเติมเช่น MaxLength ข้อแตกต่างเพียงอย่างเดียวคือแอตทริบิวต์ StringLength สามารถใช้ได้กับคุณสมบัติประเภทสตริงของคลาสโดเมนเท่านั้น

public class Course {

   public int CourseID { get; set; }
   [StringLength (24)]
   public string Title { get; set; }
   public int Credits { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Entity Framework ยังตรวจสอบค่าของคุณสมบัติสำหรับแอตทริบิวต์ StringLength หากผู้ใช้ตั้งค่า Title ซึ่งมีอักขระมากกว่า 24 ตัว EF จะโยน EntityValidationError

ตาราง

หลักการเริ่มต้นของ Code First สร้างชื่อตารางที่คล้ายกับชื่อคลาส หากคุณปล่อยให้ Code First สร้างฐานข้อมูลและต้องการเปลี่ยนชื่อของตารางที่กำลังสร้าง จากนั้น -

  • คุณสามารถใช้ Code First กับฐานข้อมูลที่มีอยู่ แต่ไม่ใช่ทุกกรณีที่ชื่อของคลาสจะตรงกับชื่อของตารางในฐานข้อมูลของคุณ

  • แอตทริบิวต์ของตารางจะแทนที่แบบแผนเริ่มต้นนี้

  • EF Code First จะสร้างตารางที่มีชื่อที่ระบุในแอตทริบิวต์ Table สำหรับคลาสโดเมนที่กำหนด

ลองมาดูตัวอย่างต่อไปนี้ที่ชั้นเรียนมีชื่อว่า Student และตามแบบแผน Code First สันนิษฐานว่าสิ่งนี้จะจับคู่กับตารางที่ชื่อนักเรียน หากไม่เป็นเช่นนั้นคุณสามารถระบุชื่อของตารางด้วยแอตทริบิวต์ Table ดังที่แสดงในรหัสต่อไปนี้

[Table("StudentsInfo")]
public class Student {

   [Key]
   public int StdntID { get; set; }
   [Required]
   public string LastName { get; set; }
   [Required]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

ตอนนี้คุณจะเห็นว่าแอตทริบิวต์ Table ระบุตารางเป็น StudentsInfo เมื่อสร้างตารางขึ้นคุณจะเห็นชื่อตาราง StudentsInfo ดังแสดงในภาพต่อไปนี้

คุณไม่สามารถระบุชื่อตารางเท่านั้น แต่คุณยังสามารถระบุสคีมาสำหรับตารางโดยใช้แอตทริบิวต์ Table ดังที่แสดงในโค้ดต่อไปนี้

[Table("StudentsInfo", Schema = "Admin")] 
public class Student {

   [Key]
   public int StdntID { get; set; }
   [Required]
   public string LastName { get; set; }
   [Required]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

คุณสามารถดูในตัวอย่างด้านบนตารางถูกระบุด้วยสคีมาของผู้ดูแลระบบ ตอนนี้ Code First จะสร้างตาราง StudentsInfo ใน Admin schema ดังที่แสดงในภาพต่อไปนี้

คอลัมน์

นอกจากนี้ยังเหมือนกับแอตทริบิวต์ Table แต่แอตทริบิวต์ Table จะแทนที่ลักษณะการทำงานของตารางในขณะที่แอตทริบิวต์ Column จะแทนที่ลักษณะการทำงานของคอลัมน์ หลักการเริ่มต้นของ Code First สร้างชื่อคอลัมน์ที่คล้ายกับชื่อคุณสมบัติ หากคุณปล่อยให้ Code First สร้างฐานข้อมูลและต้องการเปลี่ยนชื่อคอลัมน์ในตารางของคุณด้วย จากนั้น -

  • แอตทริบิวต์ของคอลัมน์จะแทนที่หลักการเริ่มต้น

  • EF Code First จะสร้างคอลัมน์ที่มีชื่อที่ระบุในแอตทริบิวต์คอลัมน์สำหรับคุณสมบัติที่กำหนด

ลองดูตัวอย่างต่อไปนี้ซึ่งคุณสมบัตินี้มีชื่อว่า FirstMidName และตามแบบแผน Code First สันนิษฐานว่าสิ่งนี้จะแมปกับคอลัมน์ชื่อ FirstMidName

หากไม่ใช่กรณีนี้คุณสามารถระบุชื่อของคอลัมน์ด้วยแอตทริบิวต์ Column ดังที่แสดงในรหัสต่อไปนี้

public class Student {

   public int ID { get; set; }
   public string LastName { get; set; }
   [Column("FirstName")]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

คุณจะเห็นว่าคอลัมน์แอตทริบิวต์ระบุคอลัมน์เป็น FirstName เมื่อสร้างตารางขึ้นคุณจะเห็นชื่อคอลัมน์ FirstName ดังแสดงในภาพต่อไปนี้

ดัชนี

แอตทริบิวต์ Index ถูกนำมาใช้ใน Entity Framework 6.1 หากคุณใช้เวอร์ชันก่อนหน้านี้ข้อมูลในส่วนนี้จะใช้ไม่ได้

  • คุณสามารถสร้างดัชนีในคอลัมน์อย่างน้อยหนึ่งคอลัมน์โดยใช้ IndexAttribute

  • การเพิ่มแอตทริบิวต์ให้กับคุณสมบัติอย่างน้อยหนึ่งอย่างจะทำให้ EF สร้างดัชนีที่สอดคล้องกันในฐานข้อมูลเมื่อสร้างฐานข้อมูล

  • ดัชนีทำให้การดึงข้อมูลเร็วขึ้นและมีประสิทธิภาพในกรณีส่วนใหญ่ อย่างไรก็ตามการใส่ตารางหรือมุมมองที่มีดัชนีมากเกินไปอาจส่งผลต่อประสิทธิภาพของการดำเนินการอื่น ๆ เช่นการแทรกหรือการอัปเดต

  • การจัดทำดัชนีเป็นคุณลักษณะใหม่ใน Entity Framework ที่คุณสามารถปรับปรุงประสิทธิภาพของแอปพลิเคชัน Code First ของคุณได้โดยลดเวลาที่ต้องใช้ในการสืบค้นข้อมูลจากฐานข้อมูล

  • คุณสามารถเพิ่มดัชนีลงในฐานข้อมูลของคุณโดยใช้แอตทริบิวต์ดัชนีและแทนที่ค่าเริ่มต้นที่ไม่ซ้ำกันและการตั้งค่าคลัสเตอร์เพื่อให้ได้ดัชนีที่เหมาะสมที่สุดกับสถานการณ์ของคุณ

  • ตามค่าเริ่มต้นดัชนีจะถูกตั้งชื่อว่า IX_ <ชื่อคุณสมบัติ>

มาดูโค้ดต่อไปนี้ซึ่งมีการเพิ่มแอตทริบิวต์ดัชนีในคลาสหลักสูตรสำหรับเครดิต

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]
   public int Credits { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

คุณจะเห็นว่าแอตทริบิวต์ดัชนีถูกนำไปใช้กับคุณสมบัติเครดิต เมื่อสร้างตารางขึ้นคุณจะเห็น IX_Credits ใน Indexes

โดยค่าเริ่มต้นดัชนีจะไม่ซ้ำกัน แต่คุณสามารถใช้ไฟล์ IsUniqueตั้งชื่อพารามิเตอร์เพื่อระบุว่าดัชนีควรไม่ซ้ำกัน ตัวอย่างต่อไปนี้แนะนำดัชนีเฉพาะดังที่แสดงในรหัสต่อไปนี้

public class Course {
   public int CourseID { get; set; }
   [Index(IsUnique = true)]
	
   public string Title { get; set; }
   [Index]
	
   public int Credits { get; set; }
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

ForeignKey

การประชุม Code First จะดูแลความสัมพันธ์ที่พบบ่อยที่สุดในโมเดลของคุณ แต่มีบางกรณีที่ต้องการความช่วยเหลือ ตัวอย่างเช่นการเปลี่ยนชื่อของคุณสมบัติหลักในคลาสนักเรียนทำให้เกิดปัญหากับความสัมพันธ์กับคลาสการลงทะเบียน

public class Enrollment {
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }
	
   public virtual Course Course { get; set; }
   public virtual Student Student { get; set; }
}

public class Student {
   [Key]
   public int StdntID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

ในขณะที่สร้างฐานข้อมูล Code First จะเห็นคุณสมบัติ StudentID ในคลาสการลงทะเบียนและรับรู้โดยแบบแผนว่าตรงกับชื่อคลาสบวก“ ID” เป็นคีย์ต่างประเทศของคลาส Student อย่างไรก็ตามไม่มีคุณสมบัติ StudentID ในคลาส Student แต่คุณสมบัติ StdntID คือคลาส Student

วิธีแก้ปัญหานี้คือการสร้างคุณสมบัติการนำทางในการลงทะเบียนและใช้ ForeignKey DataAnnotation เพื่อช่วยให้ Code First เข้าใจวิธีสร้างความสัมพันธ์ระหว่างสองคลาสดังที่แสดงในโค้ดต่อไปนี้

public class Enrollment {
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
	
   public Grade? Grade { get; set; }
   public virtual Course Course { get; set; }
   [ForeignKey("StudentID")]
	
   public virtual Student Student { get; set; }
}

ตอนนี้คุณสามารถเห็นได้ว่าแอตทริบิวต์ ForeignKey ถูกนำไปใช้กับคุณสมบัติการนำทาง

ไม่แมป

ตามมาตรฐานเริ่มต้นของ Code First ทุกคุณสมบัติที่เป็นประเภทข้อมูลที่รองรับและซึ่งรวมถึง getters และ setters จะแสดงในฐานข้อมูล แต่นี่ไม่ใช่กรณีเสมอไปในแอปพลิเคชันของคุณ แอตทริบิวต์ NotMapped แทนที่แบบแผนเริ่มต้นนี้ ตัวอย่างเช่นคุณอาจมีคุณสมบัติในชั้นเรียนนักเรียนเช่น FatherName แต่ไม่จำเป็นต้องจัดเก็บ คุณสามารถใช้แอตทริบิวต์ NotMapped กับคุณสมบัติ FatherName ซึ่งคุณไม่ต้องการสร้างคอลัมน์ในฐานข้อมูลดังที่แสดงในรหัสต่อไปนี้

public class Student {
   [Key]
   public int StdntID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
	
   public DateTime EnrollmentDate { get; set; }
   [NotMapped]

   public int FatherName { get; set; }
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

คุณจะเห็นว่ามีการใช้แอตทริบิวต์ NotMapped กับคุณสมบัติ FatherName เมื่อสร้างตารางคุณจะเห็นว่าคอลัมน์ FatherName จะไม่ถูกสร้างขึ้นในฐานข้อมูล แต่มีอยู่ในชั้นเรียนของนักเรียน

Code First จะไม่สร้างคอลัมน์สำหรับคุณสมบัติซึ่งไม่มี getters หรือ setters ดังที่แสดงในตัวอย่างคุณสมบัติ Address and Age ของคลาส Student ต่อไปนี้

คุณสมบัติผกผัน

InverseProperty ถูกใช้เมื่อคุณมีหลายความสัมพันธ์ระหว่างคลาส ในชั้นเรียนการลงทะเบียนคุณอาจต้องการติดตามผู้ที่ลงทะเบียนหลักสูตรปัจจุบันและหลักสูตรก่อนหน้า เพิ่มคุณสมบัติการนำทางสองรายการสำหรับคลาสการลงทะเบียน

public class Enrollment {
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }
	
   public virtual Course CurrCourse { get; set; }
   public virtual Course PrevCourse { get; set; }
   public virtual Student Student { get; set; }
}

ในทำนองเดียวกันคุณจะต้องเพิ่มในชั้นเรียนหลักสูตรที่อ้างอิงโดยคุณสมบัติเหล่านี้ คลาสหลักสูตรมีคุณสมบัติการนำทางกลับไปที่คลาสการลงทะเบียนซึ่งมีการลงทะเบียนปัจจุบันและก่อนหน้าทั้งหมด

public class Course {

   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]

   public int Credits { get; set; }
   public virtual ICollection<Enrollment> CurrEnrollments { get; set; }
   public virtual ICollection<Enrollment> PrevEnrollments { get; set; }
}

Code First สร้างคอลัมน์คีย์ต่างประเทศของ {Class Name} _ {Primary Key} ถ้าคุณสมบัติ Foreign Key ไม่รวมอยู่ในคลาสเฉพาะดังที่แสดงในคลาสด้านบน เมื่อสร้างฐานข้อมูลคุณจะเห็นคีย์ต่างประเทศต่อไปนี้

อย่างที่คุณเห็นว่า Code first ไม่สามารถจับคู่คุณสมบัติในสองคลาสได้ด้วยตัวเอง ตารางฐานข้อมูลสำหรับการลงทะเบียนควรมีคีย์ต่างประเทศหนึ่งคีย์สำหรับ CurrCourse และอีกหนึ่งคีย์สำหรับ PrevCourse แต่ Code First จะสร้างคุณสมบัติคีย์ต่างประเทศสี่รายการ ได้แก่

  • CurrCourse _CourseID
  • PrevCourse _CourseID
  • Course_CourseID และ
  • Course_CourseID1

ในการแก้ไขปัญหาเหล่านี้คุณสามารถใช้คำอธิบายประกอบ InverseProperty เพื่อระบุการจัดตำแหน่งของคุณสมบัติ

public class Course {

   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]

   public int Credits { get; set; }
   [InverseProperty("CurrCourse")]

   public virtual ICollection<Enrollment> CurrEnrollments { get; set; }
   [InverseProperty("PrevCourse")]

   public virtual ICollection<Enrollment> PrevEnrollments { get; set; }
}

ดังที่คุณเห็นแอตทริบิวต์ InverseProperty ถูกนำไปใช้ในคลาสหลักสูตรข้างต้นโดยระบุคุณสมบัติอ้างอิงของคลาสการลงทะเบียนที่เป็นของ ตอนนี้ Code First จะสร้างฐานข้อมูลและสร้างคอลัมน์คีย์ต่างประเทศเพียงสองคอลัมน์ในตารางการลงทะเบียนดังที่แสดงในภาพต่อไปนี้

เราขอแนะนำให้คุณดำเนินการตามตัวอย่างข้างต้นในลักษณะทีละขั้นตอนเพื่อความเข้าใจที่ดีขึ้น