EF Code-First - Cấu hình mối quan hệ một-một

Cấu hình mối quan hệ một-một trong Entity Framework

Ở phần này, bạn sẽ tìm hiểu cách cấu hình mối quan hệ một-một giữa hai thực thể.

Chúng tôi sẽ thực hiện mối quan hệ một-một giữa thực thể StudentStudentAddress như sau:

public class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }

    public virtual StudentAddress Address { get; set; }
}
     
public class StudentAddress 
{
    public int StudentAddressId { get; set; }
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string City { get; set; }
    public int Zipcode { get; set; }
    public string State { get; set; }
    public string Country { get; set; }

    public virtual Student Student { get; set; }
}

Mối quan hệ một-một xảy ra khi khóa chính của một bảng trở thành khóa chính và khóa ngoại của một bảng khác trong cơ sở dữ liệu quan hệ như SQL Server.

Vì vậy, chúng ta cần cấu hình các thực thể ở trên để EF tạo các bảng Students và bảng StudentAddresses trong DB.

Nó sẽ tạo cột StudentId trong bảng Student là khóa chính và cột StudentAddressId trong bảng StudentAddresses vừa là khóa chính vừa là khóa ngoại.

Cấu hình mối quan hệ một-một bằng cách sử dụng các attribute chú thích dữ liệu

Trong trường hợp này, chúng tôi sẽ sử dụng các thuộc tính chú thích dữ liệu trên các thực thể StudentStudentAddress để thiết lập mối quan hệ một-một.

Nếu bạn bỏ lỡ bài viết hướng dẫn cách sử dụng attribute chú thích dữ liệu thì có thể xem tại đây:

Thực thể Student tuân theo quy ước mặc định của Code First vì nó có thuộc tính StudentId sẽ là thuộc tính khóa.

Vì vậy, chúng ta không cần phải áp dụng bất kỳ attribute chú thích dữ liệu nào trên nó bởi vì EF sẽ tạo cột StudentId là khóa chính của bảng Students trong cơ sở dữ liệu.

Đối với thực thể StudentAddress, chúng ta cần cấu hình StudentAddressId vừa là khóa chính vừa là khóa ngoại.

Thuộc tính StudentAddressId theo quy ước mặc định sẽ là khóa chính. Vì vậy, chúng ta không cần phải áp dụng bất kỳ attribute chú thích dữ liệu nào cho khóa chính.

Tuy nhiên, chúng ta cũng cần cấu hình nó là khóa ngoại của thực thể Student.

Vì vậy, chúng ta sẽ khai báo [ForeignKey("Student")] trên thuộc tính StudentAddressId để cấu hình nó làm khóa ngoại cho thực thể Student, như được trình bày dưới đây.

public class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }

    public virtual StudentAddress Address { get; set; }
}
     
public class StudentAddress 
{
    [ForeignKey("Student")]
    public int StudentAddressId { get; set; }
        
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string City { get; set; }
    public int Zipcode { get; set; }
    public string State { get; set; }
    public string Country { get; set; }

    public virtual Student Student { get; set; }
}

Như vậy là bạn đã sử dụng attribute chú thích dữ liệu để cấu hình mối quan hệ một-một giữa hai thực thể.

Lưu ý: Thực thể Student có thuộc tính điều hướng StudentAddress và thực thể StudentAddress có thuộc tính điều hướng Student. Với mối quan hệ một-một, một thực thể Student có thể được lưu trữ mà không có thực thể StudentAddress nhưng ngược lại thì không thể. EF sẽ đưa ra một ngoại lệ nếu bạn cố lưu thực thể StudentAddress mà không có thực thể Student.

Cấu hình mối quan hệ một-một bằng cách sử dụng Fluent API

Ở phần này, chúng tôi sẽ sử dụng Fluent API để định cấu hình mối quan hệ một-một giữa các thực thể StudentStudentAddress.

Nếu bạn bỏ lỡ bài viết hướng dẫn cách sử dụng Fluent API thì có thể xem ở đây:

Ví dụ sau đây thiết lập mối quan hệ một-một giữa StudentStudentAddress sử dụng Fluent API.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // Configure Student & StudentAddress entity
    modelBuilder.Entity<Student>()
                .HasOptional(s => s.Address) // Mark Address property optional in Student entity
                .WithRequired(ad => ad.Student); // mark Student property as required in StudentAddress entity. Cannot save StudentAddress without Student
}

Trong ví dụ trên, chúng ta bắt đầu với thực thể Student. Phương thức HasOptional() cấu hình thuộc tính điều hướng Address của thực thể Student là tùy chọn (không bắt buộc khi lưu thực thể Student).

Sau đó, phương thức WithRequired() cấu hình thuộc tính điều hướng Student của thực thể StudentAddress là bắt buộc (bắt buộc phải có khi lưu thực thể StudentAddress; nó sẽ đưa ra một ngoại lệ khi thực thể StudentAddress được lưu mà không có thuộc tính điều hướng Student). Điều này cũng sẽ làm cho cột StudentAddressId trở thành khóa ngoại.

Như vậy là bạn đã biết cách cấu hình mối quan hệ một-một giữa hai thực thể bằng cách sử dụng Fluent API.

EF API sẽ tạo các bảng sau trong cơ sở dữ liệu.

one-to-one relationship in code first

Ở cách cấu hình trên, một thực thể Student có thể được lưu mà không có thực thể StudentAddress nhưng ngược lại thì không thể. EF sẽ đưa ra một ngoại lệ nếu bạn cố lưu thực thể StudentAddress mà không có thực thể Student.

Tuy nhiên chúng ta có thể cấu hình mối quan hệ một-một giữa các thực thể bằng Fluent API trong đó cả hai đầu đều bắt buộc.

Nghĩa là khi lưu một đối tượng thực thể Student bắt buộc phải có đối tượng thực thể StudentAddress và ngược lại.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // Configure StudentId as FK for StudentAddress
    modelBuilder.Entity<Student>()
                .HasRequired(s => s.Address) 
                .WithRequiredPrincipal(ad => ad.Student); 

}

Trong ví dụ trên, phương thức HasRequired(s => s.Address) cấu hình thuộc tính Address của thực thể StudentAddress là bắt buộc và phương thức WithRequiredPrincipal(ad => ad.Student) ấu hình thuộc tính Student của thực thể StudentAddress là bắt buộc.

Do đó, khi bạn cố gắng lưu thực thể Student mà không có thực thể StudentAddress hoặc ngược lại thì EF sẽ ném ra một ngoại lệ. 

Tạo Mô hình dữ liệu thực thể chỉ đọc cho ví dụ trên bằng EF Power Tools. Các thực thể sẽ xuất hiện giống như sơ đồ được hiển thị bên dưới:

one-to-one relationship in code first