2012-10-29 22 views
7

Đưa ra một LINQ rất cơ bản được trả về một chế độ xem MVC, tại điểm chính xác thì việc thực thi trì hoãn bị cháy?Việc thực hiện trì hoãn LINQ có xảy ra khi hiển thị chế độ xem hoặc trước đó không?

Trong bộ điều khiển:

public ActionResult Index() 
{ 
    var model = _fooService.GetAll(); 
    return View(model); 
} 

Trong mô hình:

@foreach (var item in Model) {  
    <tr> 
     <td>@item.Bar</td> 
    </tr> 
} 

Truy vấn không được thực thi khi chúng ta gọi là _fooService.GetAll() tất nhiên, nhưng được hoãn lại đến một thời điểm nào sau đó - nhưng mà tại đó chính xác điểm là nó thực hiện?

  • Câu lệnh return View(model); trong bộ điều khiển (không giống như nó)?
  • Dòng @foreach (var item in Model) trong chế độ xem?
  • Lần đầu tiên dòng @item.Bar trong chế độ xem được nhấn?
  • Điều gì khác xảy ra ở giữa return View(model); và chế độ xem được hiển thị?

Trả lời

3

MSDN documentation giải quyết câu hỏi này trong phần thực thi truy vấn bị trì hoãn (phần nhấn mạnh của tôi).

Trong truy vấn trả về một chuỗi giá trị, biến truy vấn chính nó không bao giờ chứa kết quả truy vấn và chỉ lưu trữ truy vấn lệnh. Thực hiện các truy vấn được hoãn lại cho đến khi biến truy vấn được lặp qua trong một foreach hoặc cho mỗi vòng lặp ...

Đó thu hẹp câu trả lời cho lựa chọn 2 và 3.

foreach chỉ là cú pháp đường, bên dưới trình biên dịch viết lại như là một vòng lặp while. Có một lời giải thích khá kỹ lưỡng về những gì xảy ra here. Về cơ bản vòng lặp của bạn sẽ kết thúc tìm kiếm một cái gì đó giống như

{ 
    IEnumerator<?> e = ((IEnumerable<?>)Model).GetEnumerator(); 
    try 
    { 
    int m; // this is inside the loop in C# 5 
    while(e.MoveNext()) 
    { 
     m = (?)e.Current; 
     // your code goes here 
    } 
    } 
    finally 
    { 
    if (e != null) ((IDisposable)e).Dispose(); 
    } 
} 

Enumerator này là tiên tiến trước khi nó đạt mã của bạn bên trong vòng lặp, nên hơi trước khi bạn có thể @item.Bar.Điều đó chỉ để lại tùy chọn 2, dòng @foreach (var item in Model) (mặc dù về mặt kỹ thuật, dòng đó không tồn tại sau khi trình biên dịch được thực hiện với mã của bạn).

Tôi không kiện nếu truy vấn sẽ thực hiện trên cuộc gọi đến GetEnumerator() hoặc trên cuộc gọi đầu tiên đến e.MoveNext().


Như @pst chỉ ra trong các ý kiến, có những cách khác để kích hoạt thực thi một truy vấn, chẳng hạn như bằng cách gọi ToList, và nó có thể không trong nội bộ sử dụng một vòng lặp foreach. MSDN tài liệu loại địa chỉ này here:

Giao diện IQueryable thừa hưởng giao diện IEnumerable để nếu nó đại diện cho một truy vấn, kết quả của truy vấn có thể được liệt kê. Điều tra khiến cây biểu thức được liên kết với một đối tượng IQueryable được thực hiện. Định nghĩa "thực thi biểu thức cây" là cụ thể cho nhà cung cấp truy vấn. Ví dụ: có thể liên quan đến việc dịch cây biểu thức sang ngôn ngữ truy vấn thích hợp cho nguồn dữ liệu cơ bản. Các truy vấn không trả về số đếm được thực thi khi phương thức Execute được gọi.

hiểu biết của tôi về điều đó là một nỗ lực để liệt kê các biểu hiện sẽ gây ra nó để thực thi (có thể là thông qua một foreach hoặc một số cách khác). Làm thế nào chính xác điều đó xảy ra sẽ phụ thuộc vào việc thực hiện các nhà cung cấp.

5

Việc thực hiện truy vấn (tôi giả sử GetAll trả IQueryable) sẽ được hoãn lại vào xem, và sẽ được tháo trên dòng foreach, bởi vì đó là nơi đầu tiên mà bạn bắt đầu lặp lại trong bộ sưu tập.

Cập nhật Đối với bất kỳ ai quan tâm, đây là "bằng chứng". Tạo một lớp như thế này:

public class DiagnosticCollection<T> : System.Collections.Generic.List<T> 
{ 
    public new Enumerator GetEnumerator() 
    { 
     Debug.Print("Collection Unwrap"); 
     return base.GetEnumerator(); 
    } 
} 

thử nghiệm ở chế độ xem của bạn như thế này:

@model PlayMvc.Models.DiagnosticCollection<string> 
@{System.Diagnostics.Debug.Print("Before foreach");} 
@foreach (var item in Model) 
{ 
    System.Diagnostics.Debug.Print("After foreach"); 
    @item 
} 
+1

Tôi chỉ muốn đề cập rằng bạn có lẽ nên buộc thực hiện trong bộ điều khiển. Điều này sẽ ngăn chặn các vấn đề trong tương lai như vô tình liệt kê nó nhiều lần. – Dharun

+0

Tôi không nghĩ rằng đó là một ý tưởng tuyệt vời - nếu xem không cần tất cả dữ liệu, ví dụ: nếu lưới của nó có phân trang: nếu bạn buộc thực hiện, bạn sẽ mất các lợi ích của phân trang –

+0

Đây là điều cần phải cẩn thận khi làm việc với EntityFramework. Lần đầu tiên tôi nhận thấy hành vi này bởi vì các đối tượng đã nhận được tải lười biếng trong quan điểm, mà không chỉ tôi không cần, nhưng đã thực sự vi phạm quy tắc kinh doanh nhất định. Tôi đã kết thúc việc thêm một bộ lọc để tách các đối tượng khỏi trình quản lý trạng thái sau khi thực hiện điều khiển hoàn thành để đảm bảo rằng việc thực thi hoãn lại trong khung nhìn sẽ trả về null trên các thuộc tính tải lười và không phát ra các cuộc gọi db bổ sung. Một cái gì đó để xem ra cho ... – macsux

-2

Công việc được hoãn lại cho đến khi nó cần phải làm. Trong ví dụ của bạn, truy vấn sẽ chạy khi nó cố gắng lấy giá trị cho item.

+0

Câu trả lời của bạn là một chút sai lầm vì nó cho thấy rằng viết một vòng lặp như thế này 'foreach (var mục trong Model) {}' sẽ không thực hiện truy vấn (kể từ 'mục' là bao giờ truy cập). – R0MANARMY

2

Như một điểm làm rõ, LINQ to SQL/Entity Framework đánh giá các biểu thức truy vấn LINQ khi GetEnumerator được gọi. Điều này được sử dụng dưới sự che chở của foreach, do đó trong khi nó xuất hiện trì hoãn thực thi chờ đợi cho đến khi foreach xảy ra, nó thực sự là GetEnumerator mà foreach sử dụng thực sự là điểm thực thi chính.

+0

+1 câu trả lời đầu tiên để nói chính xác tại điểm nào xảy ra –

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