Tối ưu hóa code tầng ORM với Entity Framework và giải quyết vấn đề Cache Busting

Tối ưu hóa code tầng ORM với Entity Framework và giải quyết vấn đề Cache Busting

November 29, 2024 0 By Nam Vu

Khi làm việc với Entity Framework (EF), một trong những vấn đề mà các nhà phát triển thường gặp phải là vấn đề về cache. Điều này có thể dẫn đến việc dữ liệu không được cập nhật kịp thời sau khi đã thay đổi trong cơ sở dữ liệu, đặc biệt khi bạn sử dụng LINQ để truy vấn dữ liệu. Bài viết này sẽ tập trung vào các mẹo tối ưu hóa code tầng ORM và giải quyết các vấn đề liên quan đến cache busting trong EF.

1. Vấn đề Cache trong Entity Framework

Để minh họa vấn đề, hãy xem xét ví dụ dưới đây:

var context = new MyDbContext();
var customers = context.Customers.Where(c => c.State == "VA")
.Take(2).ToList();

Khi bạn thực hiện câu lệnh này, EF sẽ dịch truy vấn LINQ sang SQL như sau:

SELECT TOP (2)
[Extent1].[CustomerId] AS [CustomerId],
[Extent1].[Name] AS [Name],
[Extent1].[State] AS [State],
-- a bunch of other columns --
FROM [dbo].[Customers] AS [Extent1]
WHERE [Extent1].[State] = 'VA'

Kết quả truy vấn sẽ trả về:

ID Name State
--- ---- -----
850 Sam VA
851 Sue VA

Bây giờ, bạn thử thay đổi giá trị Name của một trong những bản ghi:

UPDATE Customers SET Name = 'Susan' WHERE CustomerID = 851;

Sau đó, bạn lại chạy truy vấn LINQ giống như trên:

customers = context.Customers.Where(c => c.State == "VA").Take(2).ToList();

Thật ngạc nhiên, kết quả vẫn là:

ID Name State
--- ---- -----
850 Sam VA
851 Sue VA

Dữ liệu không thay đổi, mặc dù bản ghi trong cơ sở dữ liệu đã được cập nhật. Đây là một vấn đề phổ biến do cơ chế cache của Entity Framework. EF lưu trữ các đối tượng được theo dõi trong bộ nhớ, và khi bạn thực hiện truy vấn, nó có thể trả về dữ liệu cũ thay vì truy vấn lại từ cơ sở dữ liệu.

2. Giải pháp khắc phục vấn đề Cache trong EF

Dưới đây là một số giải pháp giúp bạn tránh hoặc giải quyết vấn đề cache khi làm việc với Entity Framework:

a. Sử dụng AsNoTracking() để tắt tính năng theo dõi

Entity Framework theo mặc định sẽ theo dõi các đối tượng mà bạn truy xuất từ cơ sở dữ liệu. Điều này có nghĩa là các đối tượng đã truy xuất trước đó sẽ được lấy từ bộ nhớ cache nếu bạn thực hiện truy vấn giống nhau sau đó. Để tránh điều này, bạn có thể sử dụng AsNoTracking() để tắt theo dõi.

var customers = context.Customers
.Where(c => c.State == "VA")
.AsNoTracking()
.Take(2)
.ToList();

Khi sử dụng AsNoTracking(), EF sẽ không lưu trữ các đối tượng vào bộ nhớ cache, giúp truy vấn luôn lấy dữ liệu mới nhất từ cơ sở dữ liệu.

b. Tạo lại DbContext mới

Một cách khác để đảm bảo rằng dữ liệu mới nhất được truy xuất là khởi tạo một đối tượng DbContext mới mỗi khi bạn cần truy vấn. Điều này sẽ giải quyết vấn đề cache một cách triệt để, nhưng cũng làm tăng chi phí khi liên tục tạo đối tượng mới.

using (var context = new MyDbContext())
{
var customers = context.Customers
.Where(c => c.State == "VA")
.Take(2)
.ToList();
}

c. Sử dụng ObjectQuery với MergeOptions

Khi sử dụng ObjectQuery thay vì DBQuery, bạn có thể thiết lập MergeOptions để kiểm soát cách dữ liệu được hợp nhất giữa bộ nhớ cache và cơ sở dữ liệu. Bạn có thể đặt MergeOptions.NoTracking để tắt cache.

var query = ((IObjectContextAdapter)context).ObjectContext
.CreateObjectSet<Customer>()
.Where(c => c.State == "VA")
.MergeOption = MergeOption.NoTracking;

d. Làm mới thực thể bằng GetDatabaseValues

Nếu bạn chỉ muốn cập nhật một thực thể cụ thể từ cơ sở dữ liệu mà không cần tạo mới DbContext, bạn có thể sử dụng phương thức GetDatabaseValues để lấy dữ liệu mới nhất từ cơ sở dữ liệu.

var customer = context.Customers.Find(851);
context.Entry(customer).Reload();

Hoặc sử dụng GetDatabaseValues:

var databaseValues = context.Entry(customer).GetDatabaseValues();

e. Detach các thực thể

Nếu bạn không muốn các thực thể được theo dõi sau khi truy xuất, bạn có thể tách chúng ra khỏi DbContext bằng cách sử dụng Detach. Điều này ngăn chặn chúng được lưu trong bộ nhớ cache và giúp đảm bảo truy vấn lại sẽ trả về dữ liệu mới.

context.Entry(customer).State = EntityState.Detached;

f. Sử dụng dữ liệu cũ nếu phù hợp

Trong một số trường hợp, việc sử dụng dữ liệu cache là hoàn toàn chấp nhận được nếu bạn không yêu cầu dữ liệu phải được cập nhật ngay lập tức. Trong trường hợp đó, bạn có thể tối ưu hóa bằng cách sử dụng lại dữ liệu cache.

3. Lời kết

Entity Framework mang lại nhiều tiện ích khi làm việc với cơ sở dữ liệu, nhưng cơ chế cache của nó cũng có thể gây ra những vấn đề không mong muốn nếu không được xử lý đúng cách. Khi phát triển các ứng dụng lớn, việc hiểu rõ cơ chế cache của EF và cách giải quyết vấn đề này là rất quan trọng để đảm bảo dữ liệu luôn chính xác và cập nhật.

Hy vọng những giải pháp trên sẽ giúp bạn tránh được vấn đề cache trong EF và tối ưu hóa hiệu suất của các ứng dụng sử dụng ORM.