2011-02-04 43 views
8

Chúc mừng, tôi có câu hỏi sau đây. Tôi không tìm thấy câu trả lời chính xác cho nó, và nó thực sự thú vị với tôi. Giả sử tôi có đoạn mã sau để truy lục các bản ghi từ cơ sở dữ liệu (ví dụ để xuất nó ra tệp XML).LINQ có được đánh giá lười biếng không?

var result = from emps in dc.Employees 
    where emps.age > 21 
    select emps; 

foreach (var emp in result) { 
    // Append this record in suitable format to the end of XML file 
} 

Giả sử có một triệu hồ sơ đáp ứng các nơi điều kiện trong các mã. Chuyện gì sẽ xảy ra? Tất cả dữ liệu này sẽ được lấy ra từ SQL Server ngay lập tức với bộ nhớ thời gian chạy khi nó đạt đến cấu trúc foreach hoặc nó sẽ được truy xuất sau đó cần thiết, bản ghi đầu tiên, thứ hai. Nói cách khác, LINQ có thực sự xử lý tình huống với việc lặp qua các bộ sưu tập lớn (xem post ở đây để biết chi tiết) không?

Nếu không, cách khắc phục sự cố bộ nhớ trong trường hợp đó? Nếu tôi thực sự cần phải đi qua bộ sưu tập lớn, tôi nên làm gì? Tính toán số lượng thực tế của các phần tử trong bộ sưu tập với sự trợ giúp của hàm Count, và sau đó đọc dữ liệu từ cơ sở dữ liệu bằng các phần nhỏ. Có cách nào dễ dàng để thực hiện phân trang với khung công tác LINQ không?

Trả lời

8

Tất cả dữ liệu sẽ được truy lục từ SQL Server, cùng một lúc và được đưa vào bộ nhớ. Cách duy nhất xung quanh điều này mà tôi có thể nghĩ đến là xử lý dữ liệu theo các khối nhỏ hơn (như trang sử dụng Skip() và Take()). Nhưng, tất nhiên, điều này đòi hỏi nhiều lượt truy cập đến SQL Server.

Dưới đây là một phương pháp mở rộng phân trang LINQ tôi đã viết để làm điều này:

public static IEnumerable<TSource> Page<TSource>(this IEnumerable<TSource> source, int pageNumber, int pageSize) 
    { 
     return source.Skip((pageNumber - 1) * pageSize).Take(pageSize); 
    } 
+0

+1 Tôi sử dụng chức năng gần như giống hệt nhau để thực hiện việc này. – alex

+0

Tôi không nghĩ rằng số truy cập nhiều hơn đến SQL Server sẽ là một vấn đề hiệu suất tuyệt vời, nếu chúng ta thiết lập kích thước trang một cách chính xác. Nó sẽ là khoảng 1000 hàng cho ví dụ của tôi tôi nghĩ, và khi ứng dụng sẽ chủ yếu bận rộn với xuất khẩu, không phải với truy vấn cơ sở dữ liệu. –

+0

@ Spirit_1984 - Tôi đồng ý với bạn. Tôi sẽ có nhiều lượt truy cập vào SQL Server hơn là cố gắng tải hàng triệu hàng vào bộ nhớ. –

4

Vâng, LINQ sử dụng đánh giá lười biếng. Cơ sở dữ liệu sẽ được truy vấn khi foreach bắt đầu thực thi, nhưng nó sẽ lấy tất cả dữ liệu trong một lần (sẽ ít hiệu quả hơn khi thực hiện hàng triệu truy vấn chỉ cho một kết quả tại một thời điểm).

Nếu bạn lo lắng về việc mang lại quá nhiều kết quả trong một lần, bạn có thể sử dụng Bỏ qua và Lên trên để chỉ nhận được số lượng kết quả hạn chế tại một thời điểm (do đó phân trang kết quả của bạn).

1

Nó sẽ được truy lục khi bạn gọi ToList hoặc các phương pháp tương tự. thực hiện LINQ đã trì hoãn:

Cách - thậm chí có thực hiện chậm và tải toàn bộ bộ sưu tập từ một nguồn dữ liệu trong trường hợp của một HOẶC/M hoặc bất kỳ nhà cung cấp LINQ khác - sẽ được xác định bởi người triển khai của nguồn đối tượng LINQ. Ví dụ, một số OR/M có thể cung cấp tải chậm và điều đó có nghĩa là "toàn bộ danh sách khách hàng" của bạn sẽ giống như con trỏ và truy cập một trong các mục (nhân viên) và cũng là một thuộc tính , sẽ tự tải nhân viên hoặc thuộc tính được truy cập.

Tuy nhiên, đây là những điều cơ bản.

EDIT: Bây giờ tôi thấy đó là một điều LINQ-to-SQL ... Hoặc tôi không biết nếu tác giả của câu hỏi hiểu lầm LINQ và ông không biết LINQ không phải là LINQ-to-SQL, nhưng nó nhiều hơn một mẫu và một tính năng ngôn ngữ.

0

OK, bây giờ nhờ vào điều này answer Tôi có ý tưởng - cách trộn chức năng lấy trang với lợi nhuận khả năng?Dưới đây là các mẫu mã:

// This is the original function that takes the page 
public static IEnumerable<TSource> Page<TSource>(this IEnumerable<TSource> source, int pageNumber, int pageSize) { 
    return source.Skip((pageNumber - 1) * pageSize).Take(pageSize); 
} 

// And here is the function with yield implementation 
public static IEnumerable<TSource> Lazy<TSource>(this IEnumerable<TSource> source, int pageSize) { 
    int pageNumber = 1; 
    int count = 0; 

    do { 
    IEnumerable<TSource> coll = Page(source, pageNumber, pageSize); 
    count = coll.Count(); 
    pageNumber++; 
    yield return coll; 
    } while (count > 0); 
} 


// And here goes our code for traversing collection with paging and foreach 
var result = from emps in dc.Employees 
    where emps.age > 21 
    select emps; 

// Let's use the 1000 page size 
foreach (var emp in Lazy(result, 1000)) { 
    // Append this record in suitable format to the end of XML file 
} 

Tôi nghĩ rằng cách này chúng ta có thể khắc phục vấn đề bộ nhớ, tuy nhiên rời khỏi syntaxis của foreach không quá phức tạp.

Các vấn đề liên quan