Làm thế nào tôi nên khai báo mối quan hệ chính đối ngoại bằng cách sử dụng khung thực thể đầu tiên mã (4.1) trong MVC3?


104

Tôi đã tìm kiếm tài nguyên về cách khai báo mối quan hệ khóa ngoài và các ràng buộc khác bằng cách sử dụng mã EF 4.1 đầu tiên mà không gặp nhiều may mắn. Về cơ bản, tôi đang xây dựng mô hình dữ liệu bằng mã và sử dụng MVC3 để truy vấn mô hình đó. Mọi thứ hoạt động thông qua MVC, điều đó thật tuyệt vời (kudo đối với Microsoft!) Nhưng bây giờ tôi muốn nó KHÔNG hoạt động vì tôi cần phải có các ràng buộc về mô hình dữ liệu.

Ví dụ, tôi có một đối tượng Order có rất nhiều thuộc tính là các đối tượng bên ngoài (bảng). Ngay bây giờ tôi có thể tạo một Đơn hàng không có vấn đề gì, nhưng không thể thêm khóa ngoại hoặc các đối tượng bên ngoài. MVC3 thiết lập điều này không có vấn đề.

Tôi nhận ra rằng tôi chỉ có thể tự thêm các đối tượng trong lớp bộ điều khiển trước khi lưu, nhưng tôi muốn lệnh gọi đến DbContext.SaveChanges () không thành công nếu các mối quan hệ ràng buộc không được đáp ứng.

THÔNG TIN MỚI

Vì vậy, đặc biệt, tôi muốn một ngoại lệ xảy ra khi tôi cố gắng lưu một đối tượng Order mà không chỉ định đối tượng khách hàng. Đây dường như không phải là hành vi nếu tôi chỉ soạn các đối tượng như được mô tả trong hầu hết các tài liệu về Code First EF.

Mã mới nhất:

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; }
}

Đây là lỗi tôi gặp phải khi truy cập chế độ xem được tạo VS cho Bệnh nhân:

THÔNG BÁO LỖI

Thuộc tính ForeignKeyAttribute trên thuộc tính 'Bệnh nhân' trên loại 'PhysicianPortal.Models.Order' không hợp lệ. Không tìm thấy tên khóa ngoại 'Parent' trên loại phụ thuộc 'PhysicianPortal.Models.Order'. Giá trị Tên phải là danh sách các tên thuộc tính khóa ngoại được phân tách bằng dấu phẩy.

Trân trọng,

Guido

Câu trả lời:


164

Nếu bạn có một Orderlớp, việc thêm một thuộc tính tham chiếu đến một lớp khác trong mô hình của bạn chẳng hạn Customersẽ đủ để cho EF biết rằng có một mối quan hệ trong đó:

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

    // Some other properties

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

Bạn luôn có thể đặt FKmối quan hệ một cách rõ ràng:

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; }
}

Hàm ForeignKeyAttributetạo nhận một chuỗi làm tham số: nếu bạn đặt nó trên thuộc tính khóa ngoại, nó đại diện cho tên của thuộc tính điều hướng được liên kết. Nếu bạn đặt nó trên thuộc tính điều hướng, nó đại diện cho tên của khóa ngoại liên quan.

Có gì phương tiện này là, nếu bạn biết nơi để đặt ForeignKeyAttributetrên Customertài sản, thuộc tính sẽ mất CustomerIDtrong các nhà xây dựng:

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

CHỈNH SỬA dựa trên mã mới nhất Bạn gặp lỗi đó do dòng này:

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

EF sẽ tìm kiếm một thuộc tính được gọi Parentđể sử dụng nó làm người thực thi Khoá ngoại. Bạn có thể làm 2 điều:

1) Loại bỏ ForeignKeyAttributevà thay thế nó bằng RequiredAttributeđể đánh dấu mối quan hệ theo yêu cầu:

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

Việc trang trí một thuộc tính với cái RequiredAttributecũng có một tác dụng phụ: Mối quan hệ trong cơ sở dữ liệu được tạo với ON DELETE CASCADE.

Tôi cũng khuyên bạn nên tạo thuộc tính virtualđể bật Lazy Loading.

2) Tạo một thuộc tính được gọi Parentsẽ đóng vai trò là Khóa ngoại. Trong trường hợp đó, có lẽ sẽ hợp lý hơn khi gọi nó ParentID(bạn cũng cần phải thay đổi tên ForeignKeyAttribute):

public int ParentID { get; set; }

Theo kinh nghiệm của tôi trong trường hợp này, mặc dù nó hoạt động tốt hơn nếu làm theo cách khác:

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

public virtual Patient Patient { get; set; }

Cảm ơn Sergi - Tôi đã thêm một số thông tin bổ sung trong phần trích dẫn khối.
Guido Anselmi

@Guido - Tôi đã cập nhật câu trả lời của mình dựa trên chỉnh sửa mã mới nhất của bạn, hy vọng điều này sẽ hữu ích.
Sergi Papaseit

30

Bạn có thể xác định khóa ngoại bằng cách:

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; }
}

Bây giờ ParentId là thuộc tính khóa ngoại và xác định mối quan hệ bắt buộc giữa con và mẹ hiện có. Lưu đứa trẻ mà không thoát khỏi cha mẹ sẽ ném ra ngoại lệ.

Nếu tên thuộc tính FK của bạn không bao gồm tên thuộc tính điều hướng và tên PK chính, bạn phải sử dụng chú thích dữ liệu ForeignKeyAttribute hoặc API thông thạo để ánh xạ mối quan hệ

Chú thích dữ liệu:

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

API thông thạo:

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

Các loại ràng buộc khác có thể được thực thi bằng chú thích dữ liệu và xác thực mô hình .

Biên tập:

Bạn sẽ nhận được một ngoại lệ nếu bạn không đặt ParentId. Nó là thuộc tính bắt buộc (không thể null). Nếu bạn không đặt nó, rất có thể nó sẽ cố gắng gửi giá trị mặc định đến cơ sở dữ liệu. Giá trị mặc định là 0 vì vậy nếu bạn không có khách hàng có Id = 0, bạn sẽ có một ngoại lệ.


Cảm ơn Ladislav - Tôi đã thêm một số thông tin bổ sung trong phần trích dẫn khối.
Guido Anselmi

@Ladislav. Vì vậy, để thực thi ràng buộc này, tôi PHẢI có cả tham chiếu đến Parent và tham chiếu đến ParentId. Đúng không? Tôi sẽ thêm lớp thực tế ở trên để tham khảo.
Guido Anselmi

@Guido: Đó là thông tin mới. Bạn không sử dụng thuộc tính khóa ngoại. Tất cả các thuộc tính điều hướng của bạn được xử lý dưới dạng tùy chọn theo mặc định. Sử dụng bản đồ thành thạo để lập bản đồ theo yêu cầu.
Ladislav Mrnka

@Ladislav: Cảm ơn bạn một lần nữa. Tôi đang tìm hiểu xung quanh để hiểu sự khác biệt giữa việc sử dụng Chú thích dữ liệu và API thông thạo. Tôi đã thực hiện các thay đổi đối với mã ở trên phù hợp với những gì tôi nghĩ bạn đang nói. Trên đây là tất cả những gì tôi phải làm? Trân trọng.
Guido Anselmi

Không có thuộc tính ForeignKey nào xác định thuộc tính điều hướng liên quan đến thuộc tính khóa ngoại hoặc ngược lại. Bạn không có thuộc tính khóa ngoại nên bạn không thể sử dụng thuộc tính đó. Cố gắng sử dụng thuộc tính Bắt buộc trên các thuộc tính điều hướng của bạn (tôi đã không thử nghiệm nó).
Ladislav Mrnka
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.