Query LINQ chạy lazy: Where, Select chỉ mô tả phép biến đổi, dữ liệu chỉ được kéo ra khi có ai đó bắt đầu duyệt.
Mỗi lần duyệt là chạy lại toàn bộ nguồn.
public void Report(IEnumerable<Order> orders)
{
if (!orders.Any()) return; // enumeration #1
var total = orders.Sum(o => o.Total); // enumeration #2
foreach (var o in orders) { } // enumeration #3
}Nếu orders thực chất là db.Orders.Where(...) thì đoạn trên bắn 3 query xuống database. Nếu nguồn là một iterator yield return đọc file hoặc gọi API, chi phí nhân lên tương ứng; nguồn có side effect thì còn sai kết quả. Với nguồn chỉ duyệt được một lần (stream, IEnumerator thủ công) lần thứ hai có thể trả về rỗng.
Cách xử lý: vật chất hoá một lần rồi dùng lại.
var list = orders as IReadOnlyList<Order> ?? orders.ToList();Trong .NET, analyzer CA1851 cảnh báo đúng trường hợp này.
Nguyên tắc thực dụng: nhận IEnumerable<T> ở tham số cho linh hoạt, nhưng nếu định dùng nhiều lần thì ToList() ngay đầu method; còn khi trả về từ tầng repository thì trả List<T>/IReadOnlyList<T> để caller không vô tình duyệt lại query.