2012-10-29 9 views
7

Biorąc pod uwagę bardzo podstawowe LINQ, które jest zwracane do widoku MVC, w którym dokładnie punkcie jest odroczone wykonanie ognia?Czy odroczenie LINQ występuje podczas renderowania widoku, lub wcześniej?

W regulatorze:

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

W modelu

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

Zapytanie nie jest wykonywany, gdy wzywamy _fooService.GetAll() oczywiście, ale została odroczona do pewnego późniejszym - ale przy której dokładna punkt jest wykonany?

  • Instrukcja return View(model); w kontrolerze (nie wygląda na to)?
  • Linia @foreach (var item in Model) w widoku?
  • Po raz pierwszy linia @item.Bar w widoku jest uderzona?
  • Coś jeszcze, co dzieje się pomiędzy return View(model); a renderowanym widokiem?

Odpowiedz

3

MSDN documentation odnosi się do tego pytania w sekcji odroczonego wykonywania zapytań (podkreślenie moje).

W kwerendy, która zwraca ciąg wartości, zmienna zapytania sama nigdy nie przechowuje wyniki kwerendy i tylko przechowuje zapytaniu poleceń. Wykonanie zapytania jest odroczona aż zmiennej zapytania się powtórzyć w ciągu foreach lub pętli For Each ...

To zawęża odpowiedź wariantów 2 i 3.

foreach jest po prostu cukier syntaktyczny, pod kompilatorem ponownie zapisuje to jako pętlę while. Istnieje dość dokładne wyjaśnienie, co się stanie here. Zasadniczo pętla zakończy się patrząc coś takiego

{ 
    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 jest zaawansowany, zanim osiągnie swój kod wewnątrz pętli, to tak lekko, zanim pojawi się @item.Bar.Pozostaje tylko opcja 2, linia @foreach (var item in Model) (choć technicznie ta linia nie istnieje po kompilacji z twoim kodem).

Nie jestem pozwany, jeśli zapytanie zostanie wykonane podczas połączenia z numerem GetEnumerator() lub pierwszym wywołaniem e.MoveNext().


Jak @pst zwraca uwagę w komentarzach, istnieją inne sposoby, aby wywołać wykonanie zapytania, takie jak wywołując ToList, a to nie może wewnętrznie użyć foreach pętlę. MSDN dokumentacja rodzaju rozwiązuje ten here:

IQueryable interfejs dziedziczy interfejs IEnumerable tak, że jeśli reprezentuje kwerendy, wyniki tej kwerendy mogą być wymienione. Wyliczenie powoduje wykonanie drzewa wyrażeń skojarzonego z obiektem IQueryable . Definicja "wykonywania drzewa wyrażenia " jest specyficzna dla dostawcy zapytań. Na przykład może to obejmować tłumaczenie drzewa wyrażeń na odpowiedni język zapytania dla bazowego źródła danych. Kwerendy, które nie zwracają wyliczalnych wyników są wykonywane po wywołaniu metody Execute.

Moje zrozumienia, że ​​jest to próba wyliczyć wyrażenia spowoduje to wykonać (czy to poprzez foreach lub jakiś inny sposób). Jak dokładnie to się stanie zależy od implementacji dostawcy.

5

Wykonanie zapytania (zakładam, że GetAll zwróci iqueryable) zostanie odroczone do widoku i zostanie rozpakowane w linii foreach, ponieważ jest to pierwsze miejsce, w którym rozpoczyna się iterowanie nad kolekcją.

Aktualizacja Dla wszystkich zainteresowanych, oto "dowód". Utwórz klasę tak:

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

przetestować go w widoku tak:

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

Po prostu chcę wspomnieć, że prawdopodobnie należy wymusić wykonanie w kontrolerze. Zapobiegnie to problemom w przyszłości, jak przypadkowe wielokrotne wyliczanie go. – Dharun

+0

Nie sądzę, że jest to świetny pomysł - co, jeśli widok nie potrzebuje wszystkich danych, np. Jeśli jest siatka z przywoływaniem: jeśli wymuszasz wykonanie, tracisz zalety tego stronicowania –

+0

To jest coś, na co należy uważać podczas pracy z EntityFramework. Po raz pierwszy zauważyłem to zachowanie, ponieważ obiekty były leniwie załadowane w widoku, którego nie tylko nie potrzebowałem, ale faktycznie łamało pewne reguły biznesowe. Skończyło się na dodaniu filtru do odłączania obiektów od menedżera stanu po zakończeniu wykonywania kontrolera, aby upewnić się, że odroczone wykonanie w widoku zwróci wartość null w leniwych właściwościach ładowania i nie wyda dodatkowych wywołań db. Coś, na co trzeba uważać ... – macsux

-2

Praca jest odroczona do momentu potrzebuje do zrobienia. W twoim przykładzie zapytanie zostanie uruchomione, gdy spróbuje uzyskać wartość dla item.

+0

Twoja odpowiedź jest trochę myląca, ponieważ sugeruje, że napisanie takiej pętli jak foreach (element var w modelu) {} 'nie wykonałoby zapytania (ponieważ' item' jest zawsze dostępny). – R0MANARMY

2

W celu wyjaśnienia, LINQ do SQL/Entity Framework ocenia wyrażenia zapytania LINQ po wywołaniu GetEnumerator. To jest używane pod osłonami przez foreach, a zatem, podczas gdy wydaje się, że odroczone wykonanie czeka aż do zakończenia foreach, to tak naprawdę jest to GetEnumerator, który narzuca użycie, który tak naprawdę jest kluczowym punktem wykonywania.

+0

+1 pierwsza odpowiedź, aby powiedzieć, w jakim dokładnie punkcie następuje wykonanie –

Powiązane problemy