รหัสแรก: การเชื่อมโยงอิสระกับสมาคมคีย์ต่างประเทศ?


102

ฉันถกเถียงกับตัวเองทุกครั้งที่เริ่มทำงานในโครงการใหม่และฉันกำลังออกแบบ POCO ของฉัน ฉันได้เห็นบทเรียน / ตัวอย่างโค้ดมากมายที่ดูเหมือนจะสนับสนุนการเชื่อมโยงคีย์ต่างประเทศ :

สมาคมคีย์ต่างประเทศ

public class Order
{
    public int ID { get; set; }
    public int CustomerID { get; set; } // <-- Customer ID
    ...
}

ตรงข้ามกับการเชื่อมโยงอิสระ :

สมาคมอิสระ

public class Order
{
    public int ID { get; set; }
    public Customer Customer { get; set; } // <-- Customer object
    ...
}

ฉันเคยทำงานร่วมกับ NHibernate มาก่อนและใช้การเชื่อมโยงที่เป็นอิสระซึ่งไม่เพียง แต่รู้สึก OO มากขึ้นเท่านั้น แต่ยังมีข้อได้เปรียบในการให้สิทธิ์เข้าถึงอ็อบเจ็กต์ของลูกค้าทั้งหมดแทน ID สิ่งนี้ทำให้ฉันสามารถดึงอินสแตนซ์คำสั่งซื้อจากนั้นทำได้Order.Customer.FirstNameโดยไม่ต้องทำการเข้าร่วมอย่างชัดเจนซึ่งสะดวกมาก

เพื่อสรุปคำถามของฉันคือ:

  1. มีข้อเสียที่สำคัญในการใช้การเชื่อมโยงอิสระหรือไม่? และ...
  2. ถ้าไม่มีเลยเหตุผลของการใช้การเชื่อมโยงคีย์ต่างประเทศคืออะไร?

คำตอบ:


106

หากคุณต้องการใช้ประโยชน์จาก ORM อย่างเต็มที่คุณจะต้องใช้การอ้างอิงเอนทิตี:

public class Order
{
    public int ID { get; set; }
    public Customer Customer { get; set; } // <-- Customer object
    ...
}

เมื่อคุณสร้างแบบจำลองเอนทิตีจากฐานข้อมูลด้วย FK แล้วจะสร้างการอ้างอิงเอนทิตีเสมอ หากคุณไม่ต้องการใช้คุณต้องแก้ไขไฟล์ EDMX ด้วยตนเองและเพิ่มคุณสมบัติที่แสดงถึง FKs อย่างน้อยก็เป็นกรณีนี้ใน Entity Framework v1 ที่อนุญาตเฉพาะการเชื่อมโยงอิสระเท่านั้น

Entity framework v4 เสนอการเชื่อมโยงชนิดใหม่ที่เรียกว่า Foreign key Association ความแตกต่างที่ชัดเจนที่สุดระหว่างการเชื่อมโยงอิสระและคีย์ต่างประเทศอยู่ในระดับคำสั่ง:

public class Order
{
    public int ID { get; set; }
    public int CustomerId { get; set; }  // <-- Customer ID
    public Customer Customer { get; set; } // <-- Customer object
    ...
}

อย่างที่คุณเห็นคุณมีทั้งคุณสมบัติ FK และการอ้างอิงเอนทิตี มีความแตกต่างเพิ่มเติมระหว่างการเชื่อมโยงสองประเภท:

สมาคมอิสระ

  • มันแสดงเป็นวัตถุแยกต่างหากในObjectStateManager. มีเป็นของตัวเองEntityState!
  • เมื่อสร้างการเชื่อมโยงคุณจำเป็นต้องมีสิทธิ์จากทั้งสองด้านของการเชื่อมโยงเสมอ
  • การเชื่อมโยงนี้ถูกแมปในลักษณะเดียวกับเอนทิตี

สมาคมคีย์ต่างประเทศ

  • มันไม่ได้แสดงเป็นวัตถุแยกต่างหากในObjectStateManager. เนื่องจากคุณต้องปฏิบัติตามกฎพิเศษบางประการ
  • เมื่อสร้างการเชื่อมโยงคุณไม่จำเป็นต้องมีการเชื่อมโยงทั้งสองด้าน เพียงพอที่จะมีเอนทิตีลูกและ PK ของเอนทิตีแม่ แต่ค่า PK ต้องไม่ซ้ำกัน ดังนั้นเมื่อใช้การเชื่อมโยงคีย์ต่างประเทศคุณต้องกำหนด ID เฉพาะชั่วคราวให้กับเอนทิตีที่สร้างขึ้นใหม่ที่ใช้ในความสัมพันธ์
  • การเชื่อมโยงนี้ไม่ได้ถูกแมป แต่จะกำหนดข้อ จำกัด ในการอ้างอิงแทน

หากคุณต้องการใช้การเชื่อมโยงคีย์ต่างประเทศคุณต้องเลือกรวมคอลัมน์คีย์ต่างประเทศในโมเดลในตัวช่วยสร้างโมเดลข้อมูลเอนทิตี

แก้ไข:

ฉันพบว่าความแตกต่างระหว่างความสัมพันธ์ทั้งสองประเภทนี้ไม่เป็นที่รู้จักมากนักดังนั้นฉันจึงเขียนบทความสั้น ๆ ที่ครอบคลุมเรื่องนี้พร้อมรายละเอียดเพิ่มเติมและความคิดเห็นของฉันเกี่ยวกับเรื่องนี้


ขอบคุณสำหรับคำตอบที่ชาญฉลาดและสำหรับการทำความเข้าใจเกี่ยวกับคำศัพท์ที่ถูกต้องซึ่งช่วยให้ฉันพบแหล่งข้อมูลมากมายเกี่ยวกับเรื่องนี้และข้อดี / ข้อเสียของทั้งสองเทคนิค
Daniel Liuzzi

1
ฉันเพิ่งเจอบทความของคุณ Ladislav การอ่านที่น่าสนใจมากและแหล่งข้อมูลที่ดีในการทำความเข้าใจความแตกต่างระหว่างสองแนวทางนี้ ไชโย
Daniel Liuzzi

1
@GaussZ: อย่างที่ฉันรู้ว่าไม่มีการเปลี่ยนแปลงใด ๆ ในวิธีการจัดการความสัมพันธ์ตั้งแต่ EF4 (ที่มีการแนะนำการเชื่อมโยง FK)
Ladislav Mrnka

1
คำตอบนี้และคำตอบอื่น ๆ ดูเหมือนจะไม่เกี่ยวข้องกับประสิทธิภาพ อย่างไรก็ตามตามหัวข้อ2.2 ปัจจัยที่มีผลต่อประสิทธิภาพของ View Generationจากบทความ MSDN การใช้ Independent Association ดูเหมือนจะเพิ่มต้นทุนของ View Generation มากกว่าการเชื่อมโยง Foreign Key
Veverke

1
@LadislavMrnka: คุณสามารถตรวจสอบลิงก์ไปยังงานศิลปะที่คุณกล่าวถึงข้างต้นได้หรือไม่ ฉันไม่สามารถเข้าถึงได้
Veverke

34

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

public class Order
{
  public int ID { get; set; }
  public int CustomerID { get; set; }
  public virtual Customer Customer { get; set; } // <-- Customer object
  ...
}

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


5
เห็นด้วย นี่คือสิ่งที่ฉันทำตามที่ Ladislav แนะนำ มันมอบสิ่งที่ดีที่สุดให้กับคุณทั้งสองโลก วัตถุทั้งหมดเมื่อคุณต้องการคุณสมบัติทั้งหมดและ ID ของมันเมื่อคุณต้องการเพียง PK และไม่สนใจส่วนที่เหลือ
Daniel Liuzzi

9

การเชื่อมโยงอิสระไม่ได้ผลดีกับAddOrUpdateที่มักใช้ในSeedวิธีการ เมื่อข้อมูลอ้างอิงเป็นรายการที่มีอยู่แล้วจะมีการแทรกใหม่

// Existing customer.
var customer = new Customer { Id = 1, Name = "edit name" };
db.Set<Customer>().AddOrUpdate(customer);

// New order.
var order = new Order { Id = 1, Customer = customer };
db.Set<Order>().AddOrUpdate(order);

ผลลัพธ์คือลูกค้าปัจจุบันจะถูกใส่เข้าไปใหม่และลูกค้าใหม่ (แทรกใหม่) จะเชื่อมโยงกับคำสั่งซื้อใหม่


เว้นแต่เราจะใช้การเชื่อมโยงคีย์ต่างประเทศและกำหนดรหัส

 // Existing customer.
var customer = new Customer { Id = 1, Name = "edit name" };
db.Set<Customer>().AddOrUpdate(customer);

// New order.
var order = new Order { Id = 1, CustomerId = customer.Id };
db.Set<Order>().AddOrUpdate(order);

เรามีพฤติกรรมที่คาดหวังลูกค้าที่มีอยู่จะเชื่อมโยงกับคำสั่งซื้อใหม่


2
นี่คือการค้นพบที่ดี อย่างไรก็ตามวิธีแก้ปัญหา (และฉันคิดว่าวิธีที่ถูกต้อง) ในการแนบลูกค้าเพื่อสั่งซื้อคือการโหลดจากบริบทฐานข้อมูลเช่นนี้var order = new Order { Id = 1, Customer = db.Customers.Find(1) }; หรือคุณสามารถใช้วิธีการเลือกเพื่อโหลดลูกค้าจากบริบทฐานข้อมูล สิ่งนี้ใช้ได้กับการเชื่อมโยงอิสระ
tala9999

4

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

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