Eager loading trong Entity Framework – chủ động tải dữ liệu quan hệ

    0

    Eager loading là cơ chế tải dữ liệu quan hệ “thủ công” trong Entity Framework. Nếu bạn không sử dụng lazy loading từ lúc xây dựng EDM (ví dụ để entity class là sealed hoặc không sử dụng virtual cho navigation property) hoặc không muốn sử dụng lazy loading (tắt chế độ này từ DbContext), bạn cần sử dụng cơ chế eager loading.

    Trong bài học này chúng ta sẽ xem xét chi tiết cơ chế eager loading. Đây là bài học thứ hai về cách tải dữ liệu quan hệ trong Entity Framework.

    Eager loading

    Cơ chế eager loading đòi hỏi bạn phải tự mình chỉ định những dữ liệu quan hệ nào mình muốn tải sử dụng phương thức Include của DbSet.

    Hãy cùng thực hiện một ví dụ.

    Các ví dụ trong bài này tiếp tục sử dụng project bạn đã tạo trong bài học về lazy loading.
    Do EDM ban đầu được thiết kế để sử dụng lazy loading, bạn có thể tắt chế độ này bằng cách gọi context.Configuration.LazyLoadingEnabled = false; hoặc bỏ từ khóa virtual trước navigation property Teacher và Students của class Course.
    Trong ví dụ dưới đây chúng ta dùng cách thứ nhất.

    static void EagerLoading(UniversityContext context)
    {
        context.Configuration.LazyLoadingEnabled = false; // tắt chế độ lazy loading
        var courses = context.Courses.Include(c => c.Teacher); // lưu ý thêm using System.Data.Entity;
    
        foreach (var c in courses)
        {
            ForegroundColor = ConsoleColor.Green;
            Write($"{c.Name.ToUpper()}");
    
            if (c.Teacher != null)
            {
                var t = c.Teacher;
                ForegroundColor = ConsoleColor.Yellow;
                WriteLine($" ({t.Qualification} {t.FirstName} {t.LastName})");
                ResetColor();
            }
    
            if (c.Students != null && c.Students.Count > 0)
            {
                WriteLine("# Students enrolled in this course:");
                foreach (var s in c.Students)
                {
                    WriteLine($"  -> {s.FirstName} {s.LastName}");
                }
            }
        }
    }

    Lưu ý thêm using System.Data.Entity; vào đầu file code để có thể sử dụng lambda overload của Include. Nếu không bạn chỉ có thể sử dụng string overload của Include. Chúng ta sẽ nói về string overload sau.

    Trong ví dụ trên, trước hết chúng ta tạm thời tắt chế độ lazy loading. Khi tạo truy vấn để tải các khóa học, chúng ta sử dụng phương thức Include và chỉ định tải thêm Teacher (nhưng không tải thêm Students). Khi chạy chương trình bạn thu được kết quả như sau:

    ví dụ về eager loading

    Như vậy chỉ những navigation property nào được chỉ định trong Include mới được tải trong truy vấn.

    Phương thức Include có một overload khác chấp nhận tham số là chuỗi ký tự tên của navigation property. Trong một số trường hợp, overload này có vai trò rất quan trọng. “Phiên bản string” của Include rất tiện dụng nếu tên của navigation property đến từ tham số của phương thức. Khi này sử dụng “phiên bản lambda” lại rắc rối hơn nhiều. Truy vấn ở bên trên bạn cũng có thể viết lại như sau:

    var courses = context.Courses
                  .Include("Teacher")  // hoặc Include(c=>c.Teacher)
                  .Include("Students");// hoặc Include(c=>c.Students)

    Sử dụng eager loading trong truy vấn

    Nếu có nhiều navigation property cần tải, bạn có thể ghép nối nhiều phương thức Include với nhau.

    Ví dụ, nếu muốn tải thêm cả dữ liệu về giảng viên và sinh viên tham gia khóa học bạn có thể viết truy vấn như sau:

    var courses = context.Courses
                    .Include(c => c.Teacher)
                    .Include(c => c.Students);

    Bạn có thể sử dụng Include trong các truy vấn LINQ to Entities:

    static void EagerLoadingQuery(UniversityContext context)
    {
        context.Configuration.LazyLoadingEnabled = false; // tắt chế độ lazy loading
        var courses = context.Courses
            .Include(c => c.Teacher)
            .Where(c => c.Name.Contains("Magic"))
            .OrderBy(c => c.Name)
            ; // lưu ý thêm using System.Data.Entity;
        WriteLine("### Magic courses in Hogwarts ###");
        foreach (var c in courses)
        {
            ForegroundColor = ConsoleColor.Green;
            Write($"{c.Name.ToUpper()}");
    
            if (c.Teacher != null)
            {
                var t = c.Teacher;
                ForegroundColor = ConsoleColor.Yellow;
                WriteLine($" ({t.Qualification} {t.FirstName} {t.LastName})");
                ResetColor();
            }
        }
    }

    Trong ví dụ trên bạn đã viết một truy vấn LINQ to Entities để lọc ra những khóa học có từ Magic và sắp xếp chúng theo thứ tự a-b-c. Trong đó thông tin về giảng viên được tải cùng.

    eager loading trong linq to entities

    Do Include là một phương thức mở rộng của IQueryable<T> nên bạn có thể viết nó ở bất kỳ vị trí nào trong truy vấn, không nhất thiết phải viết ngay sau tên DbSet. Cách viết sau hoàn toàn không có gì khác biệt.:

    var courses = context.Courses
        .Where(c => c.Name.Contains("Magic"))
        .OrderBy(c => c.Name)
        .Include(c => c.Teacher);

    Tải dữ liệu qua nhiều cấp quan hệ

    Trong ví dụ trên khi tải Course bạn tải thêm cả dữ liệu quan hệ với nó là Teacher. Nhưng Teacher lại có quan hệ tiếp với Department, Department lại có quan hệ với School. Phương thức Include cho phép bạn tải cả những dữ liệu qua nhiều cấp quan hệ như vậy.

    Lazy loading cũng có khả năng hoàn toàn tương tự nếu bạn lần lượt truy xuất tới các navigation property của các cấp quan hệ.

    static void EagerLoadingMultiLevel(UniversityContext context)
    {
        context.Configuration.LazyLoadingEnabled = false;
        var courses = context.Courses
            .Include(c => c.Teacher.Department);
    
        foreach (var c in courses)
        {
            ForegroundColor = ConsoleColor.Green;
            Write($"{c.Name.ToUpper()}");
    
            if (c.Teacher != null)
            {
                var t = c.Teacher;
                ForegroundColor = ConsoleColor.Yellow;
                Write($" ({t.Qualification} {t.FirstName} {t.LastName}");
                ResetColor();
                Write(" from ");
                ForegroundColor = ConsoleColor.Magenta;
                WriteLine($"Department of {c.Teacher.Department.Name})");
            }
        }
    }
    eager loading nhiều cấp quan hệ

    Trong ví dụ trên phương thức Include hơi khác biệt là bạn chỉ định tải thêm cả Departement, vốn có quan hệ với Teacher:

    var courses = context.Courses
            .Include(c => c.Teacher.Department);

    Nếu dùng “phiên bản string” của Include thì truy vấn sẽ có dạng như sau:

    var courses = context.Courses
                  .Include("Teacher.Department");// hoặc Include(c=>c.Teacher.Department)

    Với chỉ định như vậy, cả Teacher và Department sẽ đều cùng được tải trong truy vấn.

    Kết luận

    Trong bài học này bạn đã tìm hiểu cách tải dữ liệu quan hệ chủ động sử dụng eager loading.

    Eager loading tạo ra ít truy vấn hơn so với lazy loading để tải cùng những lượng dữ liệu như nhau. Lý do là, eager loading sử dụng phép toán JOIN của SQL để tạo ra một truy vấn lớn lấy tất cả những dữ liệu cần thiết. Ở khía cạnh khác, lazy loading tạo ra nhiều truy vấn nhỏ để lấy những dữ liệu mà chương trình cần.

    Do truy vấn JOIN rất nặng, sử dụng eager loading không phải lúc nào cũng tốt hơn so với lazy loading ở khía cạnh hiệu suất.

    Do vậy, nếu lượng dữ liệu tải về quá lớn, bạn cần đo đạc cụ thể để quyết định tải chúng trong một truy vấn lớn (eager loading) hay tự động tải dần từng phần qua nhiều truy vấn nhỏ (lazy loading).

    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ề