Thêm/sửa/xóa quan hệ trong Entity Framework

    0

    Quan hệ giữa các object trong Entity Framework được thể hiện qua các navigation property của domain class. Do vậy, để cập nhật quan hệ giữa các object, bạn thực tế phải cập nhật các navigation property tương ứng.

    Các bài toán sẽ xem xét trong bài học này là thêm/sửa/xóa quan hệ giữa các object, và xóa object có dữ liệu quan hệ.

    Xóa object có dữ liệu quan hệ

    Nếu object bạn cần xóa có các dữ liệu quan hệ, tùy thuộc vào cấu hình của quan hệ, tình huống có thể phức tạp hơn. Nhìn chung, Entity Framework cho phép dễ dàng xóa bỏ object kể cả khi nó nằm trong quan hệ phức tạp với các object khác.

    Dependant trong quan hệ 1 – nhiều

    Nếu object cần xóa nằm ở phía “con/dependant” trong quan hệ 1 – nhiều, bạn không cần băn khoăn gì về quan hệ. Object của bạn cần xóa là object phụ. Bạn có thể thoải mái xóa nó mà không có vấn đề gì xảy ra.

    Đây là trường hợp quan hệ giữa Course Teacher trong EDM chúng ta xây dựng. Trong đó Teacher là phía cha/principal, Course là phía con/dependant.

    public virtual Teacher Teacher { get; set; }

    Như bạn đã thấy trong bài học về cập nhật dữ liệu cơ bản, bạn rất dễ dàng xóa object của Course mà không cần tải object có quan hệ (Teacher) vào chương trình.

    Quan hệ nhiều – nhiều

    Nếu object cần xóa nằm trong quan hệ nhiều – nhiều (không có payload), bạn cũng có thể thoải mái xóa do các object trong quan hệ này đều tồn tại độc lập.

    Khi xóa một object, các bản ghi tương ứng trong bảng trung gian (giúp tạo quan hệ nhiều – nhiều) sẽ tự động bị xóa theo.

    Đây là trường hợp quan hệ giữa Course Student

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

    Trong tình huống này bạn cũng không cần tải danh sách object có quan hệ (Students) vào chương trình.

    Principal trong quan hệ 1 – nhiều

    Nếu object cần xóa nằm ở phía cha/principal của quan hệ 1 – nhiều, tình hình sẽ hơi khác. Sự khác biệt này phụ thuộc vào tính bắt buộc trong quan hệ 1 – nhiều. Có hai tình huống xảy ra.

    Quan hệ không bắt buộc

    Nếu loại quan hệ là không bắt buộc, các object còn lại trong quan hệ có thể tồn tại độc lập. Khi đó bạn có thể dễ dàng xóa object chính mà không bị lỗi gì. Các object còn lại trong quan hệ vẫn tồn tại và chỉ ngắt quan hệ với object vừa bị xóa. Khóa ngoài của object con giờ sẽ nhận giá trị null.

    Ví dụ, bạn có thể xóa object của Teacher mà không ảnh hưởng đến các Course vì quan hệ giữa Teacher và Course là 1 (teacher) – nhiều (course) và không bắt buộc. Bạn có thể tạo ra Course độc lập mà không cần tham chiếu tới Teacher nào.

    private static void DeleteTeacher(UniversityContext context)
    {
        var query = context.People.OfType<Teacher>().Where(t => t.LastName == "Dumbledore").Include("Courses"); // phải tải các Course có liên quan
                
        var teacher = query.Single() as Teacher;
        WriteLine($"{teacher.Position} {teacher.FirstName} {teacher.LastName}");
        Write($"Do you want to delete this teacher? ");
        var answer = ReadKey();
        if (answer.Key == ConsoleKey.Y)
        {
            context.People.Remove(teacher);
            context.SaveChanges();
        }
    }

    Đặc biệt lưu ý: bạn phải tải các object có quan hệ với nó vào chương trình thì mới có thể xóa được. Nếu không tải các object có quan hệ, bạn sẽ gặp lỗi khi gọi SaveChanges. Nếu object có quan hệ với Teacher, đến lượt nó, lại có quan hệ với các object khác, bạn phải tải hết tất cả các object trong cả chuỗi quan hệ lớn này.

    Quan hệ bắt buộc

    Nếu quan hệ là bắt buộc và object cần xóa nằm ở phía cha/principal, bạn không thể xóa ngay nó được. Các object ở phía “con” không thể tồn tại độc lập và khóa ngoài của nó không thể nhận giá trị null. Nếu bạn xóa object “cha” sẽ dẫn đến lỗi.

    Trong trường hợp này bạn có hai lựa chọn: (1) tự mình xử lý các object con bằng cách xóa hoặc chuyển quan hệ sang một object cha khác; (2) sử dụng cơ chế cascade delete để tự động xóa các object con.

    Trong Entity Framework, cascade delete được tự động sử dụng.

    Cascade delete có hai loại: client-side do Entity Framework chịu trách nhiệm với các object mà context đang theo dõi; server-side do Sql Server chịu trách nhiệm. Mặc định, Entity Framework luôn bật chế độ cascade delete cho client-side, đồng thời cấu hình cho cả server side trong quan hệ 1 – nhiều bắt buộc.

    Để client-side cascade delete có thể hoạt động, bạn bắt buộc phải tải tất cả các object có quan hệ.

    Nếu bạn đồng thời cấu hình server-side cascade delete, bạn không cần tải các object có quan hệ. Khi này việc xóa các object phụ trong quan hệ sẽ do cơ sở dữ liệu chịu trách nhiệm.

    Sau đây là một ví dụ:

    private static void DeleteDepartment(UniversityContext context)
    {    
        var dep = context.Departments
            .Include(d => d.Teachers.Select(t=>t.Courses))
            .FirstOrDefault();
        context.Departments.Remove(dep);
        context.SaveChanges();
    }

    Trong đoạn code trên chúng ta cần xóa Department đầu tiên trong cơ sở dữ liệu. Department có quan hệ 1 – nhiều bắt buộc với Teacher. Do cơ chế cascade delete mặc định, về lý thuyết bạn có thể xóa object của Department và tất cả các object Teacher có quan hệ với nó. Tuy nhiên, bản thân Teacher lại có quan hệ với Course. Bạn bắt buộc phải tải trọn vẹn cả chuỗi quan hệ Department – Teacher – Course vào chương trình, mặc dù Course sẽ không bị xóa đi.

    Khi này, object của Department và các Teacher nằm trong Department đó sẽ bị xóa. Tuy nhiên, Course sẽ không bị ảnh hưởng vì quan hệ Course và Teacher là nhiều – nhiều (không bắt buộc).

    Thêm/thay đổi quan hệ giữa các object

    Trước hết hãy cùng xem một ví dụ:

    private static void AddRelation(UniversityContext context)
    {
        var school = context.Schools.First();
        var departments = new Department[]
        {
            new Department { Name = "Applied Mathematics", School = school},
            new Department { Name = "Cybernetics", School = school},
            new Department { Name = "Cyber Security", School = school}
        }; 
        context.Departments.AddRange(departments);
    
        var teachers = new Teacher[]
        {
            new Teacher { FirstName = "Donald", LastName = "Trump", Department = departments[0]},
            new Teacher { FirstName = "Barack", LastName = "Obama", Department = departments[0], IsDean = true},
            new Teacher { FirstName = "George", LastName = "Bush", Department = departments[1]},
        };
        context.People.AddRange(teachers);
    
        context.SaveChanges();
    }

    Trong ví dụ trên chúng ta tạo ra 3 object Department và 3 object Teacher, đồng thời tạo ra quan hệ giữa chúng: Donald Trump và Barack Obama làm việc cho khoa Applied Mathematics, còn George Bush – khoa Cybernetics.

    Bạn để ý thấy rằng, trong một mối quan hệ 1 – nhiều sẽ bao gồm hai phía: phía principal sẽ chứa collection của dependant; phía dependant chứa 1 object của principal. Ngoài ra, phía dependant còn có thể chứa Id của principal. Nói cách khác, có thể có đến 3 thành phần trong một quan hệ giữa hai object.

    Trong Entity Framework, bạn không cần gán giá trị cho cả 3 thành phần đó. Bạn chỉ cần gán giá trị cho một trong ba thành phần trên là đủ. Entity Framework sẽ tự động cập nhật cho các thành phần còn lại. Cơ chế này có tên gọi là relationship fix-up.

    Trong ví dụ trên chúng ta thiết lập quan hệ giữa Teacher và Department từ phía Teacher bằng cách gán giá trị tương ứng cho property Department. Sở dĩ chúng ta làm vậy là vì departments được khai báo trước.

    Nếu bạn khai báo teachers trước, bạn có thể xây dựng mối quan hệ theo cách sau:

    var teachers = new Teacher[]
    {
    new Teacher { FirstName = "Donald", LastName = "Trump"},
    new Teacher { FirstName = "Barack", LastName = "Obama", IsDean = true},
    new Teacher { FirstName = "George", LastName = "Bush"},
    };
    context.People.AddRange(teachers);
    
    var departments = new Department[]
    {
    new Department { Name = "Applied Mathematics", School = school, Teachers = new[]{ teachers[0], teachers[1]} },
    new Department { Name = "Cybernetics",School = school, Teachers = new[] { teachers[2]} },
    new Department { Name = "Cyber Security", School = school,}
    };
    context.Departments.AddRange(departments);

    Cả hai cách đem lại cùng một kết quả.

    Cách tạo quan hệ của Entity Framework có thể sẽ hơi gây ngạc nhiên nếu bạn xuất phát từ khía cạnh lập trình hướng đối tượng. Lý do là, trong lập trình hướng đối tượng, tùy thuộc vào cách bạn gán giá trị, bạn có thể chỉ truy cập được các object có quan hệ theo một chiều. Tuy nhiên, do cơ chế relationship fix-up, dù bạn chỉ gán giá trị một lần, bạn có thể truy xuất object có quan hệ theo cả hai chiều.

    Việc thay đổi quan hệ giữa các object thực hiện không có gì khác biệt so với khi thêm quan hệ (thực ra có thể xem chúng là một).

    Kết luận

    Trong bài học này chúng ta đã học cách thêm/sửa/xóa quan hệ giữa các object trong Entity Framework. Trong đó, thêm/sửa quan hệ thực hiện hoàn toàn tương tự như khi bạn làm việc với các object thông thường. Khi xóa object chứa quan hệ, cần lưu ý những gì có thể xảy ra tùy thuộc vào quan hệ.

    Bạn có thể download mã nguồn để tham khảo.

    Nếu có thắc mắc hoặc cần trao đổi thêm, mời bạn viết trong phần Bình luận ở cuối trang. Nếu cần trao đổi riêng, hãy gửi email hoặc nhắn tin qua form liên hệ. Nếu bài viết hữu ích với bạn, hãy giúp chúng tôi chia sẻ tới mọi người. Cảm ơn bạn!

    Bình luận

    avatar
      Đăng ký theo dõi  
    Thông báo về