ฉันจะประกาศความสัมพันธ์คีย์ต่างประเทศโดยใช้ Code First Entity Framework (4.1) ใน MVC3 ได้อย่างไร


104

ฉันค้นหาแหล่งข้อมูลเกี่ยวกับวิธีการประกาศความสัมพันธ์ของคีย์ต่างประเทศและข้อ จำกัด อื่น ๆ โดยใช้รหัส EF 4.1 แรกโดยไม่ต้องมีโชคมากนัก โดยทั่วไปฉันกำลังสร้างโมเดลข้อมูลในโค้ดและใช้ MVC3 เพื่อค้นหาโมเดลนั้น ทุกอย่างทำงานผ่าน MVC ซึ่งยอดเยี่ยมมาก (ขอชื่นชม Microsoft!) แต่ตอนนี้ฉันไม่ต้องการให้มันทำงานเพราะฉันต้องมีข้อ จำกัด ของโมเดลข้อมูล

ตัวอย่างเช่นฉันมีวัตถุ Order ที่มีคุณสมบัติมากมายที่เป็นวัตถุภายนอก (ตาราง) ตอนนี้ฉันสามารถสร้างคำสั่งซื้อได้โดยไม่มีปัญหา แต่ไม่สามารถเพิ่ม Foreign Key หรือวัตถุภายนอกได้ MVC3 ตั้งค่านี้ไม่มีปัญหา

ฉันรู้ว่าฉันสามารถเพิ่มวัตถุด้วยตัวเองในคลาสคอนโทรลเลอร์ก่อนที่จะบันทึก แต่ฉันต้องการให้การเรียกไปยัง DbContext.SaveChanges () ล้มเหลวหากไม่ตรงตามความสัมพันธ์ของข้อ จำกัด

ข้อมูลใหม่

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

รหัสล่าสุด:

public class Order
{
    public int Id { get; set; }

    [ForeignKey( "Parent" )]
    public Patient Patient { get; set; }

    [ForeignKey("CertificationPeriod")]
    public CertificationPeriod CertificationPeriod { get; set; }

    [ForeignKey("Agency")]
    public Agency Agency { get; set; }

    [ForeignKey("Diagnosis")]
    public Diagnosis PrimaryDiagnosis { get; set; }

    [ForeignKey("OrderApprovalStatus")]
    public OrderApprovalStatus ApprovalStatus { get; set; }

    [ForeignKey("User")]
    public User User { get; set; }

    [ForeignKey("User")]
    public User Submitter { get; set; }

    public DateTime ApprovalDate { get; set; }
    public DateTime SubmittedDate { get; set; }
    public Boolean IsDeprecated { get; set; }
}

นี่เป็นข้อผิดพลาดที่ฉันได้รับในขณะนี้เมื่อเข้าถึงมุมมอง VS ที่สร้างขึ้นสำหรับผู้ป่วย:

ข้อความผิดพลาด

ForeignKeyAttribute ในทรัพย์สิน 'ผู้ป่วย' ในประเภท 'PhysicianPortal.Models.Order' ไม่ถูกต้อง ไม่พบชื่อคีย์ต่างประเทศ 'Parent' ในประเภทที่ขึ้นกับ 'PhysicianPortal.Models.Order' ค่า Name ควรเป็นรายการชื่อคุณสมบัติคีย์ต่างประเทศที่คั่นด้วยเครื่องหมายจุลภาค

ความนับถือ,

กุยโด

คำตอบ:


164

ตัวอย่างเช่นหากคุณมีOrderคลาสการเพิ่มคุณสมบัติที่อ้างอิงคลาสอื่นในโมเดลของคุณCustomerก็น่าจะเพียงพอที่จะแจ้งให้ EF ทราบว่ามีความสัมพันธ์อยู่ในนั้น:

public class Order
{
    public int ID { get; set; }

    // Some other properties

    // Foreign key to customer
    public virtual Customer Customer { get; set; }
}

คุณสามารถตั้งค่าFKความสัมพันธ์อย่างชัดเจนได้ตลอดเวลา:

public class Order
{
    public int ID { get; set; }

    // Some other properties

    // Foreign key to customer
    [ForeignKey("Customer")]
    public string CustomerID { get; set; }
    public virtual Customer Customer { get; set; }
}

ตัวForeignKeyAttributeสร้างรับสตริงเป็นพารามิเตอร์: ถ้าคุณวางไว้บนคุณสมบัติคีย์นอกนั้นแทนชื่อของคุณสมบัติการนำทางที่เกี่ยวข้อง หากคุณวางไว้บนคุณสมบัติการนำทางมันแสดงถึงชื่อของคีย์ต่างประเทศที่เกี่ยวข้อง

สิ่งนี้หมายความว่าถ้าคุณจะวางForeignKeyAttributeบนCustomerคุณสมบัติไว้ที่ใดแอตทริบิวต์จะCustomerIDอยู่ในตัวสร้าง:

public string CustomerID { get; set; }
[ForeignKey("CustomerID")]
public virtual Customer Customer { get; set; }

แก้ไขตามรหัสล่าสุด คุณได้รับข้อผิดพลาดนั้นเนื่องจากบรรทัดนี้:

[ForeignKey("Parent")]
public Patient Patient { get; set; }

EF จะมองหาคุณสมบัติที่เรียกว่าParentใช้เป็น Foreign Key enforcer คุณสามารถทำได้ 2 อย่าง:

1) ลบForeignKeyAttributeและแทนที่ด้วยRequiredAttributeเพื่อทำเครื่องหมายความสัมพันธ์ตามต้องการ:

[Required]
public virtual Patient Patient { get; set; }

ตกแต่งสถานที่ให้บริการที่RequiredAttributeยังมีผลข้างเคียงที่ดี: ON DELETE CASCADEความสัมพันธ์ในฐานข้อมูลถูกสร้างขึ้นด้วย

ฉันขอแนะนำให้สร้างคุณสมบัติvirtualเพื่อเปิดใช้งาน Lazy Loading

2) สร้างคุณสมบัติที่เรียกParentว่าจะใช้เป็น Foreign Key ในกรณีนี้อาจเหมาะสมกว่าที่จะเรียกมันเช่นParentID(คุณจะต้องเปลี่ยนชื่อForeignKeyAttributeด้วย):

public int ParentID { get; set; }

จากประสบการณ์ของฉันในกรณีนี้แม้ว่าจะทำงานได้ดีกว่าหากใช้วิธีอื่น:

[ForeignKey("Patient")]
public int ParentID { get; set; }

public virtual Patient Patient { get; set; }

ขอบคุณ Sergi - ฉันได้เพิ่มข้อมูลเพิ่มเติมในคำพูดของบล็อก
Guido Anselmi

@Guido - ฉันได้อัปเดตคำตอบตามการแก้ไขโค้ดล่าสุดของคุณหวังว่านี่จะช่วยได้
Sergi Papaseit

30

คุณสามารถกำหนดคีย์ต่างประเทศได้โดย:

public class Parent
{
   public int Id { get; set; }
   public virtual ICollection<Child> Childs { get; set; }
}

public class Child
{
   public int Id { get; set; }
   // This will be recognized as FK by NavigationPropertyNameForeignKeyDiscoveryConvention
   public int ParentId { get; set; } 
   public virtual Parent Parent { get; set; }
}

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

หากชื่อคุณสมบัติ FK ของคุณไม่มีชื่อคุณสมบัติการนำทางและชื่อ PK หลักคุณต้องใช้คำอธิบายประกอบข้อมูล ForeignKeyAttribute หรือ Fluent API เพื่อแมปความสัมพันธ์

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

// The name of related navigation property
[ForeignKey("Parent")]
public int ParentId { get; set; }

API ที่คล่องแคล่ว:

modelBuilder.Entity<Child>()
            .HasRequired(c => c.Parent)
            .WithMany(p => p.Childs)
            .HasForeignKey(c => c.ParentId);

ประเภทอื่น ๆ ของข้อ จำกัด สามารถบังคับใช้โดยคำอธิบายประกอบข้อมูลและการตรวจสอบรูปแบบ

แก้ไข:

ParentIdคุณจะได้รับการยกเว้นถ้าคุณไม่ได้ตั้งค่า เป็นคุณสมบัติที่ต้องการ (ไม่เป็นโมฆะ) หากคุณไม่ได้ตั้งค่าไว้ส่วนใหญ่อาจพยายามส่งค่าเริ่มต้นไปยังฐานข้อมูล ค่าเริ่มต้นคือ 0 ดังนั้นหากคุณไม่มีลูกค้าที่มี Id = 0 คุณจะได้รับข้อยกเว้น


ขอบคุณ Ladislav - ฉันได้เพิ่มข้อมูลเพิ่มเติมในข้อความอ้างอิงของบล็อก
Guido Anselmi

@ คุณ Ladislav. ดังนั้นเพื่อบังคับใช้ข้อ จำกัด นี้ฉันต้องมีทั้งการอ้างอิงถึง Parent และการอ้างอิงถึง ParentId ถูกต้องหรือไม่ ฉันจะเพิ่มคลาสจริงด้านบนเพื่อใช้อ้างอิง
Guido Anselmi

@ Guido: นั่นคือข้อมูลใหม่ คุณไม่ได้ใช้คุณสมบัติคีย์ต่างประเทศ คุณสมบัติการนำทางทั้งหมดของคุณได้รับการจัดการเป็นทางเลือกตามค่าเริ่มต้น ใช้การทำแผนที่อย่างคล่องแคล่วเพื่อแมปตามความต้องการ
Ladislav Mrnka

@Ladislav: ขอบคุณอีกครั้ง ฉันกำลังมองหาเพื่อทำความเข้าใจความแตกต่างระหว่างการใช้คำอธิบายประกอบข้อมูลและ Fluent API ฉันทำการเปลี่ยนแปลงโค้ดด้านบนตามที่ฉันคิดว่าคุณกำลังพูด สิ่งที่ต้องทำข้างต้นคืออะไร? ความนับถือ.
Guido Anselmi

ไม่มีแอตทริบิวต์ ForeignKey กำหนดคุณสมบัติการนำทางที่เกี่ยวข้องกับคุณสมบัติคีย์ต่างประเทศหรือในทางกลับกัน คุณไม่มีคุณสมบัติคีย์ต่างประเทศดังนั้นคุณจึงไม่สามารถใช้แอตทริบิวต์นั้นได้ ลองใช้แอตทริบิวต์ที่จำเป็นกับคุณสมบัติการนำทางของคุณ (ฉันไม่ได้ทดสอบ)
Ladislav Mrnka
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.