Khi một query Include từ hai collection navigation trở lên ở cùng cấp, EF dịch thành JOIN, và kết quả là tích Descartes của các collection đó.
// blog with 10 posts and 10 contributors -> 100 rows for ONE blog
var blogs = await db.Blogs
.Include(b => b.Posts)
.Include(b => b.Contributors)
.ToListAsync();Mọi cột của Blogs bị lặp lại 100 lần trên đường truyền. Số bản ghi nhân lên theo tích, nên với dữ liệu thật thời gian và bộ nhớ tăng rất nhanh.
AsSplitQuery() bảo EF tách thành nhiều câu SQL — một cho entity gốc, một cho mỗi collection — rồi ghép ở client:
var blogs = await db.Blogs
.Include(b => b.Posts)
.Include(b => b.Contributors)
.AsSplitQuery()
.ToListAsync();Đánh đổi: nhiều round-trip hơn tới DB, và các câu SQL không nằm trong cùng một transaction nên dữ liệu có thể không nhất quán nếu có ghi xen giữa. Với luồng đọc thuần thì hiếm khi là vấn đề; cần chắc chắn thì bọc trong transaction serializable.
Quy tắc thực hành: một collection Include thì để single query; từ hai trở lên thì cân nhắc split. Có thể bật mặc định bằng UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery) và ghi đè từng query bằng AsSingleQuery().