Mã đầu tiên: Hiệp hội độc lập so với Hiệp hội khóa nước ngoài?


102

Tôi luôn tranh luận về tinh thần với chính mình mỗi khi bắt đầu làm việc với một dự án mới và tôi đang thiết kế các POCO của mình. Tôi đã thấy nhiều hướng dẫn / mẫu mã có vẻ ưu tiên các liên kết khóa nước ngoài :

Hiệp hội khóa nước ngoài

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

Trái ngược với các hiệp hội độc lập :

Hiệp hội độc lập

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

Tôi đã làm việc với NHibernate trước đây và sử dụng các liên kết độc lập, không chỉ cảm thấy OO hơn mà còn (với tính năng tải chậm) có lợi thế là cho tôi quyền truy cập vào toàn bộ đối tượng Khách hàng, thay vì chỉ ID của nó. Điều này cho phép tôi, ví dụ, truy xuất một cá thể Order và sau đó thực hiện Order.Customer.FirstNamemà không cần phải thực hiện phép nối một cách rõ ràng, điều này cực kỳ thuận tiện.

Tóm lại, câu hỏi của tôi là:

  1. Có bất kỳ nhược điểm đáng kể nào trong việc sử dụng các hiệp hội độc lập không? và ...
  2. Nếu không có bất kỳ, lý do sử dụng các liên kết khóa nước ngoài là gì?

Câu trả lời:


106

Nếu bạn muốn tận dụng tối đa ORM, bạn chắc chắn sẽ sử dụng tham chiếu Thực thể:

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

Khi bạn tạo một mô hình thực thể từ cơ sở dữ liệu với FK, nó sẽ luôn tạo ra các tham chiếu thực thể. Nếu bạn không muốn sử dụng chúng, bạn phải sửa đổi thủ công tệp EDMX và thêm các thuộc tính đại diện cho FK. Ít nhất thì đây là trường hợp trong Entity Framework v1, nơi chỉ các hiệp hội độc lập mới được phép.

Entity framework v4 cung cấp một loại liên kết mới được gọi là liên kết khóa ngoại. Sự khác biệt rõ ràng nhất giữa liên kết độc lập và liên kết khóa ngoại là trong lớp Order:

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

Như bạn có thể thấy, bạn có cả thuộc tính FK và tham chiếu thực thể. Có nhiều sự khác biệt hơn giữa hai loại liên kết:

Hiệp hội độc lập

  • Nó được biểu diễn dưới dạng đối tượng riêng biệt trong ObjectStateManager. Nó có riêng của nó EntityState!
  • Khi xây dựng liên kết, bạn luôn cần sự lôi kéo từ cả hai đầu của liên kết
  • Liên kết này được ánh xạ theo cùng một cách với thực thể.

Hiệp hội khóa nước ngoài

  • Nó không được biểu thị như một đối tượng riêng biệt trong ObjectStateManager. Do đó bạn phải tuân theo một số quy tắc đặc biệt.
  • Khi xây dựng liên kết, bạn không cần cả hai đầu của liên kết. Chỉ cần có thực thể con và PK của thực thể mẹ nhưng giá trị PK phải là duy nhất. Vì vậy, khi sử dụng kết hợp khóa ngoại, bạn cũng phải gán các ID duy nhất tạm thời cho các thực thể mới được tạo được sử dụng trong quan hệ.
  • Sự liên kết này không được ánh xạ mà thay vào đó nó xác định các ràng buộc tham chiếu.

Nếu bạn muốn sử dụng kết hợp khóa ngoài, bạn phải đánh dấu chọn Bao gồm các cột khóa ngoài trong mô hình trong Trình hướng dẫn Mô hình Dữ liệu Thực thể.

Biên tập:

Tôi nhận thấy rằng sự khác biệt giữa hai loại hiệp hội này không được nhiều người biết đến nên tôi đã viết một bài báo ngắn trình bày chi tiết hơn và ý kiến ​​của riêng tôi về điều này.


Cảm ơn câu trả lời rất sâu sắc của bạn, và cũng vì những hiểu biết về thuật ngữ chính xác, điều này đã giúp tôi tìm thấy nhiều tài nguyên về chủ đề này và ưu / nhược điểm của cả hai kỹ thuật.
Daniel Liuzzi

1
Tôi vừa xem qua bài báo của bạn, Ladislav. Bài đọc rất thú vị và nguồn tài liệu tuyệt vời để hiểu thêm sự khác biệt giữa hai cách tiếp cận này. Chúc mừng.
Daniel Liuzzi

1
@GaussZ: Như tôi biết không có bất kỳ thay đổi nào trong cách xử lý các liên kết kể từ EF4 (nơi các liên kết FK được giới thiệu).
Ladislav Mrnka

1
Câu trả lời này và các câu trả lời khác dường như không liên quan đến hiệu suất. Tuy nhiên, theo mục 2.2 Các yếu tố ảnh hưởng đến hiệu suất của Chế độ xem từ một bài báo MSDN, việc sử dụng Hiệp hội độc lập dường như làm tăng chi phí của Chế độ xem so với các liên kết Khoá nước ngoài.
Veverke

1
@LadislavMrnka: bạn có thể kiểm tra kỹ liên kết đến artice mà bạn đề cập ở trên đang hoạt động không? Tôi không thể truy cập nó.
Veverke

34

Sử dụng cả hai. Và làm cho các tham chiếu thực thể của bạn ảo để cho phép tải chậm. Như thế này:

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

Điều này giúp tiết kiệm các tra cứu DB không cần thiết, cho phép tải chậm và cho phép bạn dễ dàng xem / đặt ID nếu bạn biết mình muốn nó là gì. Lưu ý rằng có cả hai không thay đổi cấu trúc bảng của bạn theo bất kỳ cách nào.


5
Đã đồng ý. Đây là những gì tôi đã kết thúc, như Ladislav đề nghị. Nó thực sự mang lại cho bạn điều tốt nhất của cả hai thế giới; toàn bộ đối tượng khi bạn cần tất cả các thuộc tính của nó và ID của nó khi bạn chỉ cần PK và không quan tâm đến phần còn lại.
Daniel Liuzzi

9

Liên kết độc lập không hoạt động tốt với AddOrUpdateđiều đó thường được sử dụng trong Seedphương pháp. Khi tham chiếu là một mục hiện có, nó sẽ được chèn lại.

// 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);

Kết quả là khách hàng hiện tại sẽ được chèn lại và khách hàng mới (được chèn lại) sẽ được liên kết với đơn hàng mới.


Trừ khi chúng tôi sử dụng liên kết khóa ngoại và gán id.

 // 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);

Chúng tôi có hành vi mong đợi, khách hàng hiện tại sẽ được liên kết với đơn đặt hàng mới.


2
Đây là một phát hiện tốt. Tuy nhiên, công việc xung quanh (và tôi nghĩ là đúng đắn) để đính kèm một khách hàng vào đơn đặt hàng là tải nó từ ngữ cảnh db như sau: var order = new Order { Id = 1, Customer = db.Customers.Find(1) }; Hoặc bạn có thể sử dụng phương pháp Chọn để tải khách hàng từ ngữ cảnh db. Điều này hoạt động với hiệp hội độc lập.
tala9999

4

Tôi ủng hộ cách tiếp cận đối tượng để tránh những tra cứu không cần thiết. Các đối tượng thuộc tính có thể dễ dàng phổ biến khi bạn gọi phương thức gốc của mình để xây dựng toàn bộ thực thể (sử dụng mã gọi lại đơn giản cho các thực thể lồng nhau). Không có nhược điểm nào mà tôi có thể thấy ngoại trừ việc sử dụng bộ nhớ (nhưng bạn sẽ lưu vào bộ nhớ cache các đối tượng của mình phải không?). Vì vậy, tất cả những gì bạn đang làm là thay thế ngăn xếp cho đống và tăng hiệu suất từ ​​việc không thực hiện tra cứu. Tôi hy vọng điều này có ý nghĩa.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.