EF Code-First - Cascade Delete

Cascade Delete trong Code First

Cascade Delete sẽ tự động xóa các bản ghi phụ thuộc hoặc thiết lập các cột khóa ngoại thành null khi bản ghi cha bị xóa trong cơ sở dữ liệu.

Cascade Delete được bật theo mặc định trong Entity Framework cho tất cả các loại mối quan hệ, chẳng hạn như một-một, một-nhiều và nhiều-nhiều.

Cascade Delete trong mối quan hệ một-một

Hãy xem ví dụ sau đây: các thực thể StudentStudentAddress có mối quan hệ một-một.

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

Ví dụ sau đây minh họa cascade delete.

using (var ctx = new SchoolContext()) 
{
    var stud = new Student() { StudentName = "James" };
    var add = new StudentAddress() { Address1 = "address" };

    stud.Address = add;

    ctx.Students.Add(stud);

    ctx.SaveChanges();
    
    ctx.Students.Remove(stud);// student and its address will be removed from db

    ctx.SaveChanges();
}

Trong ví dụ trên, đầu tiên EF lưu đối tượng stud của thực thể Student và đối tượng add của thực thể StudentAddress của nó vào cơ sở dữ liệu.

Sau đó, khi xóa đối tượng stud và gọi phương thức SaveChanges(), EF sẽ xóa stud cũng như bản ghi tương ứng của nó trong bảng StudentAddresses.

Do đó, EF cho phép cascade delete theo mặc định.

Cascade Delete trong mối quan hệ một-nhiều

Hãy xem ví dụ sau đây: các thực thể StudentStandard có mối quan hệ một-nhiều.

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

    public virtual Standard Standard { get; set; }
}
       
public class Standard
{
    public Standard()
    {
        Students = new List<Student>();
    }
    public int StandardId { get; set; }
    public string Description { get; set; }

    public virtual ICollection<Student> Students { get; set; }
}

Ví dụ sau đây minh họa cascade delete giữa các thực thể có mối quan hệ một-nhiều:

using (var ctx = new SchoolContext()) 
{

    var student1 = new Student() { StudentName = "James" };
    var student2 = new Student() { StudentName = "Gandhi" };

    var standard1 = new Standard() { StandardName = "Standard 1" };

    student1.Standard = standard1;
    student2.Standard = standard1;

    ctx.Students.Add(student1);
    ctx.Students.Add(student2);
                
    //inserts students and standard1 into db
    ctx.SaveChanges();

    //deletes standard1 from db and also set standard_StandardId FK column in Students table to null for
    // all the students that reference standard1.
    ctx.Standards.Remove(standard1);

    ctx.SaveChanges();
}

Trong ví dụ trên, EF xóa đối tượng standard1 khỏi cơ sở dữ liệu và nó cũng thiết lập cột khóa ngoại standard_StandardId trong bảng Students thành null cho tất cả các bản ghi tham chiếu đối tượng standard1.

Lưu ý: EF tự động xóa các bản ghi liên quan trong bảng ở giữa cho các thực thể có mối quan hệ nhiều-nhiều nếu một thực thể bị xóa.

Do đó, EF cho phép cascade delete mặc định cho tất cả các thực thể.

Tắt Cascade Delete

Sử dụng Fluent API để cấu hình tắt cascade delete cho các thực thể để bằng phương thức WillCascadeOnDelete(), như ví dụ bên dưới.


public class SchoolContext<: DbContext
{
    public SchoolContext():base("MySchool")
    {
    }

    public DbSet<Student> Students { get; set; }
    public DbSet<Standard> Standards { get; set; }
        
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Student>()
            .HasOptional<Standard>(s => s.Standard)
            .WithMany()
            .WillCascadeOnDelete(false);
    }
}
Lưu ý: Không có attribute chú thích dữ liệu nào có sẵn để tắt cascade delete.