Tối ưu hiệu năng Entity Framework Core trong ứng dụng .NET

1. Sử dụng các hàm cơ sở dữ liệu trực tiếp qua EF.Functions

Khi thực hiện truy vấn chuỗi, việc dùng EF.Functions.Like thường cho hiệu suất tốt hơn so với các phương thức tích hợp như StartsWith, Contains hay EndsWith, vì nó sinh ra câu lệnh SQL sử dụng toán tử LIKE — vốn được tối ưu bởi hầu hết các hệ quản trị cơ sở dữ liệu. Ví dụ:
  • Contains("abc") thường được dịch thành biểu thức dùng CHARINDEX (trên SQL Server), dẫn đến việc không thể tận dụng chỉ mục đầy đủ:
var results = context.Users.Where(u => u.Name.Contains("abc")).ToList();
  • Trong khi đó, EF.Functions.Like sinh ra câu lệnh LIKE '%abc%', hỗ trợ kế hoạch thực thi hiệu quả hơn nếu có chỉ mục phù hợp:
var results = context.Users
    .Where(u => EF.Functions.Like(u.Name, "%abc%"))
    .ToList();
Ngoài ra, các hàm ngày tháng như EF.Functions.DateDiffDay, EF.Functions.DateDiffHour, hoặc EF.Functions.DateDiffMonth giúp tính khoảng cách giữa hai thời điểm mà không cần tải dữ liệu về bộ nhớ để xử lý — toàn bộ phép tính diễn ra tại phía máy chủ CSDL.

2. Mở rộng chức năng với thư viện Z.EntityFramework.Plus.EFCore

Gói mở rộng miễn phí này cung cấp các phương thức khối (bulk operations) giúp tránh vòng lặp và truy vấn dư thừa:
  • Xóa hàng loạt không cần tải thực thể trước:
context.Orders.Where(o => o.Status == "Cancelled").DeleteFromQuery();
  • Cập nhật trực tiếp trên cơ sở dữ liệu:
context.Customers
    .Where(c => c.LastLogin < DateTime.UtcNow.AddDays(-30))
    .Update(c => new Customer { IsActive = false });
Lưu ý: Các phiên bản nâng cao (như Batch Delete/Update với điều kiện phức tạp hoặc hỗ trợ transaction) yêu cầu giấy phép trả phí.

3. Ưu tiên FindAsync() thay vì FirstOrDefaultAsync() khi tra cứu theo khóa chính

Phương thức FindAsync() kiểm tra đầu tiên trong bộ nhớ đệm (change tracker) trước khi truy vấn cơ sở dữ liệu. Điều này đặc biệt hữu ích khi cùng một thực thể đã được tải trước đó trong cùng scope của DbContext. Ngược lại, FirstOrDefaultAsync() luôn phát hành truy vấn mới tới CSDL — gây tổn thất hiệu năng không cần thiết.

4. Vô hiệu hóa theo dõi thực thể khi chỉ đọc dữ liệu

Theo mặc định, EF Core lưu trữ bản sao trạng thái của mọi thực thể được truy vấn nhằm hỗ trợ cập nhật tự động. Nếu bạn chỉ cần đọc dữ liệu (ví dụ: hiển thị danh sách báo cáo), việc tắt cơ chế theo dõi sẽ giảm đáng kể chi phí bộ nhớ và thời gian khởi tạo đối tượng:
var products = await context.Products.AsNoTracking().ToListAsync();
Để áp dụng toàn cục cho một lần truy vấn đa bảng, có thể cấu hình hành vi theo dõi ngay từ đầu:
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

5. Dùng AnyAsync() thay vì CountAsync() hoặc FirstOrDefaultAsync() khi kiểm tra sự tồn tại

Khi mục tiêu chỉ là xác định xem tập kết quả có phần tử nào hay không, AnyAsync() dừng ngay sau khi tìm thấy phần tử đầu tiên — tương đương với EXISTS trong SQL. Trong khi đó, CountAsync() buộc phải đếm toàn bộ kết quả, và FirstOrDefaultAsync() vẫn tạo đối tượng dù chỉ cần kiểm tra tính tồn tại.
bool hasActiveOrder = await context.Orders.AnyAsync(o => o.UserId == userId && o.Status == "Processing");

Thẻ: ef-core SQL-Server entity-framework Performance-Optimization linq-to-entities

Đăng vào ngày 10 tháng 7 lúc 23:56