Ví dụ mối quan hệ một-nhiều

Các mối quan hệ một-một xảy ra khi có chính xác một bản ghi trong bảng đầu tiên tương ứng với một bản ghi trong bảng có liên quan. Ví dụ: công dân Hoa Kỳ có số An sinh Xã hội. Chỉ có một số được gán cho mỗi người, và do đó, một người không thể có nhiều số.

Dưới đây là một ví dụ khác bằng cách sử dụng hai bảng bên dưới. Các bảng có mối quan hệ một-một vì mỗi hàng trong bảng đầu tiên có liên quan trực tiếp đến một hàng khác trong bảng thứ hai.

Ở phần này, chúng ta sẽ tìm hiểu cách cấu hình mối quan hệ một-nhiều giữa hai thực thể trong Entity Framework 6.x bằng cách sử dụng phương pháp tiếp cận Code First.

Chúng ta sẽ cấu hình mối quan hệ một-nhiều giữa các thực thể

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
1 và
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
2 - một lớp có nhiều sinh viên.

public class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }
}
       
public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}

Sau khi thực thi mối quan hệ một-nhiều trong các thực thể trên, các bảng cơ sở dữ liệu cho Student và Grade sẽ giống như bên dưới. 


Các quy ước cho mối quan hệ một-nhiều

Có một số quy ước nhất định trong Entity Framework mà nếu các lớp thực thể tuân theo sẽ tự động dẫn đến mối quan hệ một-nhiều giữa hai bảng trong cơ sở dữ liệu. Bạn không cần phải cấu hình bất cứ điều gì khác.

Hãy xem một ví dụ về các quy ước tạo ra mối quan hệ một-nhiều trong Entity Framework Code First.

Quy ước 1:

Chúng tôi muốn thiết lập mối quan hệ một-nhiều giữa các thực thể

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
1 và
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
2. Có nghĩa là mỗi thực thể
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
1 sẽ trỏ đến một thực thể
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
2.

Điều này có thể thực hiện bằng cách tạo một thuộc tính điều hướng tham chiếu kiểu

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
2 trong lớp thực thể
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
1, như ví dụ bên dưới.

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}

Trong ví dụ trên, lớp

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
1 có một thuộc tính điều hướng tham chiếu của lớp
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
2. Vì vậy, có thể có nhiều học sinh trong một lớp.

Điều này sẽ dẫn đến mối quan hệ một-nhiều giữa bảng

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

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }

    public ICollection Students { get; set; } 
}
1 và bảng
public class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }

    public ICollection Students { get; set; } 
}
2 trong cơ sở dữ liệu, trong đó bảng
public class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }

    public ICollection Students { get; set; } 
}
1 có khóa ngoại
public class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }

    public ICollection Students { get; set; } 
}
4 như hình bên dưới.

Lưu ý rằng thuộc tính tham chiếu là nullable, vì vậy nó tạo ra một cột khóa ngoại

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

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }

    public ICollection Students { get; set; } 
}
4 có thể null trong bảng
public class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }

    public ICollection Students { get; set; } 
}
1.

Quy ước 2:

Có một thuộc tính điều hướng kiểu tập hợp trong thực thể chính như dưới đây.

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

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }

    public ICollection Students { get; set; } 
}

Trong ví dụ trên, thực thể

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
2 có một thuộc tính điều hướng tập hợp kiểu
public class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }

    public ICollection Students { get; set; } 
}
8.

Điều này cũng dẫn đến mối quan hệ một-nhiều giữa các thực thể

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
1 và
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
2. Ví dụ này tạo ra kết quả tương tự trong cơ sở dữ liệu như quy ước 1.

Quy ước 3:

Có các thuộc tính điều hướng ở cả hai đầu cũng sẽ dẫn đến mối quan hệ một-nhiều, như được trình bày bên dưới.

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeID { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
    
    public ICollection Student { get; set; }
}

Trong ví dụ trên, lớp thực thể

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
1 có một thuộc tính điều hướng tham chiếu kiểu
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
2 và lớp thực thể
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
2 có một thuộc tính điều hướng tập hợp kiểu
public class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }

    public ICollection Students { get; set; } 
}
8 dẫn đến mối quan hệ một-nhiều. Ví dụ này tạo ra kết quả tương tự trong cơ sở dữ liệu như quy ước 1.

Quy ước 4:

Một mối quan hệ được chỉ định đầy đủ ở cả hai đầu sẽ tạo ra mối quan hệ một-nhiều, như hình dưới đây.

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    public int GradeId { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{

    public int GradeId { get; set; }
    public string GradeName { get; set; }
    
    public ICollection Student { get; set; }
}

Trong ví dụ trên, lớp thực thể

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
1 có thuộc tính khóa ngoại
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeID { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
    
    public ICollection Student { get; set; }
}
6 và thuộc tính tham chiếu của nó là
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
2. Điều này sẽ tạo mối quan hệ một-nhiều với cột khóa ngoại NotNull trong bảng
public class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }

    public ICollection Students { get; set; } 
}
1, như được hiển thị bên dưới.

Nếu kiểu dữ liệu của

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeID { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
    
    public ICollection Student { get; set; }
}
6 là số nguyên nullable, thì nó sẽ tạo khóa ngoại null.

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }

    public int? GradeId { get; set; }
    public Grade Grade { get; set; }
}

Đoạn mã trên sẽ tạo một cột

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeID { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
    
    public ICollection Student { get; set; }
}
6 có thể null trong cơ sở dữ liệu vì chúng tôi đã sử dụng kiểu
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    public int GradeId { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{

    public int GradeId { get; set; }
    public string GradeName { get; set; }
    
    public ICollection Student { get; set; }
}
1 (
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    public int GradeId { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{

    public int GradeId { get; set; }
    public string GradeName { get; set; }
    
    public ICollection Student { get; set; }
}
2 là viết tắt của
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    public int GradeId { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{

    public int GradeId { get; set; }
    public string GradeName { get; set; }
    
    public ICollection Student { get; set; }
}
1)


Cấu hình mối quan hệ một-nhiều bằng Fluent API

Nói chung, bạn không cần định cấu hình mối quan hệ một-nhiều trong Entity Framework vì các quy ước ở trên sẽ giúp bạn làm điều này.

Tuy nhiên, bạn có thể định cấu hình các mối quan hệ bằng Fluent API tại một nơi để làm cho nó dễ bảo trì hơn.

Hãy xem các lớp thực thể

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
1 và
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
2 sau.

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }

    public int CurrentGradeId { get; set; }
    public Grade CurrentGrade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }

    public ICollection Students { get; set; }
}

Bạn có thể cấu hình mối quan hệ một-nhiều cho các thực thể ở trên bằng Fluent API bằng cách ghi đè phương thức

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    public int GradeId { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{

    public int GradeId { get; set; }
    public string GradeName { get; set; }
    
    public ICollection Student { get; set; }
}
6 trong lớp Context, như ví dụ bên dưới.

public class SchoolContext : DbContext
{
    public DbSet Students { get; set; }
    public DbSet Grades { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // configures one-to-many relationship
        modelBuilder.Entity()
            .HasRequired(s => s.CurrentGrade)
            .WithMany(g => g.Students)
            .HasForeignKey(s => s.CurrentGradeId);
    }
}

Chúng ta hãy hiểu mã trên từng bước.

  • Đầu tiên chúng ta cần chỉ định cấu hình cho lớp thực thể nào bằng khai báo
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        
        public int GradeId { get; set; }
        public Grade Grade { get; set; }
    }
    
    public class Grade
    {
    
        public int GradeId { get; set; }
        public string GradeName { get; set; }
        
        public ICollection Student { get; set; }
    }
    
    7 - cấu hình cho thực thể
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Grade Grade { get; set; }
    }
    
    public class Grade
    {
        public int GradeId { get; set; }
        public string GradeName { get; set; }
        public string Section { get; set; }
    }
    
    1.
  • Khai báo
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        
        public int GradeId { get; set; }
        public Grade Grade { get; set; }
    }
    
    public class Grade
    {
    
        public int GradeId { get; set; }
        public string GradeName { get; set; }
        
        public ICollection Student { get; set; }
    }
    
    9 chỉ định rằng lớp thực thể
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Grade Grade { get; set; }
    }
    
    public class Grade
    {
        public int GradeId { get; set; }
        public string GradeName { get; set; }
        public string Section { get; set; }
    }
    
    1 yêu cầu thuộc tính
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
    
        public int? GradeId { get; set; }
        public Grade Grade { get; set; }
    }
    
    1. Điều này sẽ tạo một cột khóa ngoài NotNull trong DB.
  • Khai báo
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
    
        public int? GradeId { get; set; }
        public Grade Grade { get; set; }
    }
    
    2 chỉ định rằng lớp thực thể
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Grade Grade { get; set; }
    }
    
    public class Grade
    {
        public int GradeId { get; set; }
        public string GradeName { get; set; }
        public string Section { get; set; }
    }
    
    2 có nhiều thực thể
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Grade Grade { get; set; }
    }
    
    public class Grade
    {
        public int GradeId { get; set; }
        public string GradeName { get; set; }
        public string Section { get; set; }
    }
    
    1.
  • Bây giờ, nếu thực thể
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Grade Grade { get; set; }
    }
    
    public class Grade
    {
        public int GradeId { get; set; }
        public string GradeName { get; set; }
        public string Section { get; set; }
    }
    
    1 không tuân theo quy ước thuộc tính Id cho khóa ngoại, thì chúng ta có thể chỉ định tên của khóa ngoại bằng phương thức
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
    
        public int? GradeId { get; set; }
        public Grade Grade { get; set; }
    }
    
    6. Khai báo
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
    
        public int? GradeId { get; set; }
        public Grade Grade { get; set; }
    }
    
    7 chỉ định thuộc tính khóa ngoại trong thực thể
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Grade Grade { get; set; }
    }
    
    public class Grade
    {
        public int GradeId { get; set; }
        public string GradeName { get; set; }
        public string Section { get; set; }
    }
    
    1.

Ngoài ra, bạn cũng có thể cấu hình mối quan hệ bắt đầu với thực thể

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
2 thay vì thực thể
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
1 như ở ví dụ trên. Ví dụ sau đây tạo ra kết quả tương tự như trên.

modelBuilder.Entity()
    .HasMany(g => g.Students)
    .WithRequired(s => s.CurrentGrade)
    .HasForeignKey(s => s.CurrentGradeId);
    

Ví dụ trên sẽ tạo các bảng sau trong cơ sở dữ liệu.

Cấu hình khóa ngoại NotNull bằng Fluent API

Trong quy ước 1, chúng ta đã thấy rằng nó tạo ra một mối quan hệ một-nhiều tùy chọn, từ đó tạo ra một cột khóa ngoại có thể Null trong cơ sở dữ liệu.

Để biến nó thành cột NotNull, hãy sử dụng phương thức

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }

    public int CurrentGradeId { get; set; }
    public Grade CurrentGrade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }

    public ICollection Students { get; set; }
}
1 như bên dưới.

modelBuilder.Entity()
    .HasRequired(s => s.CurrentGrade)
    .WithMany(g => g.Students);
    

Cấu hình Cascade Delete bằng Fluent API

Cascade Delete có nghĩa là tự động xóa các các bản ghi con liên quan khi bản ghi cha bị xóa. Ví dụ, nếu lớp bị xóa thì tất cả các sinh viên trong lớp đó cũng sẽ bị xóa tự động. Ví dụ sau cấu hình Cascade Delete bằng phương thức

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }

    public int CurrentGradeId { get; set; }
    public Grade CurrentGrade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }

    public ICollection Students { get; set; }
}
2.

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
0

Lưu ý: Chúng tôi khuyến nghị bạn không nên sử dụng cascade delete để tránh gặp phải vấn đề mất mát dữ liệu ngoài ý muốn.