2009-01-19 11 views
11

Wyobraźmy mam następujące:Jak LINQ odroczyć wykonanie, gdy w użyciu instrukcji

private IEnumerable MyFunc(parameter a) 
{ 
    using(MyDataContext dc = new MyDataContext) 
    { 
     return dc.tablename.Select(row => row.parameter == a); 
    } 
} 

private void UsingFunc() 
{ 
    var result = MyFunc(new a()); 

    foreach(var row in result) 
    { 
     //Do something 
    } 
} 

Zgodnie z dokumentacją wykonanie LINQ będzie odroczyć do ja rzeczywisty wyliczyć wynik, który występuje w kolejce w foreach . Jednak instrukcja using powinna wymusić wiarygodne zbieranie obiektu pod koniec wywołania MyFunct().

Co tak naprawdę się dzieje, kiedy uruchomi się rozdrabniacz i/lub jego wynik?

Jedyne o czym mogę pomyśleć to odroczone wykonanie jest obliczane w czasie kompilacji, więc faktyczne wywołanie jest przenoszone przez kompilator do pierwszej linii foreach, powodując, że użycie działa poprawnie, ale nie działa, dopóki foreach linia? Czy jest jakiś guru, który może pomóc?

EDYCJA: UWAGA: Ten kod działa, po prostu nie rozumiem w jaki sposób.

Zrobiłem trochę czytania i uświadomiłem sobie w moim kodzie, że nazwałem metodę rozszerzenia ToList(), która oczywiście wylicza wynik. Zachowanie odpowiedzi z zaznaczoną odpowiedzią jest całkowicie poprawne, aby odpowiedzieć na pytanie.

Przepraszamy za jakiekolwiek zamieszanie.

+0

ładne pytanie. Zostałem zbombardowany przez to ... a Using (DataContext) wysadza się spektakularnie – Gishu

+0

Otrzymuję ObjectDisposedException "DataContext dostępny po Dispose." kiedy próbuję tego. Czy Twój obiekt MyDataContext jest autogenerowanym DataContext od projektanta LINQ? Czy dziedziczy po DataContext? –

+0

Trzecie pojęcie: jeśli wyrzucisz swój datakekst za pomocą "Używanie", otrzymasz wyjątek środowiska wykonawczego, jeśli spróbujesz później użyć datacontekstu. Wiem, że to zrobiłem. –

Odpowiedz

12

Spodziewam się, że po prostu nie zadziała; Select jest odroczony, więc żadne dane nie zostały zużyte w tym momencie. Ponieważ jednak wyłączyłeś kontekst danych (przed opuszczeniem MyFunc), nigdy nie będzie on w stanie uzyskać danych. Lepszym rozwiązaniem jest przekazanie kontekstu danych do metody, tak aby konsument mógł wybrać czas życia. Również polecam powrocie IQueryable<T> tak, że konsument może "tworzyć" wynik (czyli dodać OrderBy/Skip/Take/Where etc, i mieć to wpływ na ostateczną zapytanie):

// this could also be an instance method on the data-context 
internal static IQueryable<SomeType> MyFunc(
    this MyDataContext dc, parameter a) 
{ 
    return dc.tablename.Where(row => row.parameter == a); 
} 

private void UsingFunc() 
{ 
    using(MyDataContext dc = new MyDataContext()) { 
     var result = dc.MyFunc(new a()); 

     foreach(var row in result) 
     { 
      //Do something 
     } 
    } 
} 

Update: jeśli ty (komentarze) nie chcesz odroczyć wykonania (tzn. nie chcesz, aby rozmówca zajmował się kontekstem danych), musisz ocenić wyniki. Możesz to zrobić, dzwoniąc pod numer .ToList() lub .ToArray(), aby buforować wartości.

private IEnumerable<SomeType> MyFunc(parameter a) 
{ 
    using(MyDataContext dc = new MyDataContext) 
    { 
     // or ToList() etc 
     return dc.tablename.Where(row => row.parameter == a).ToArray(); 
    } 
} 

Jeśli chcesz zachować to odroczone w tym przypadku, to trzeba użyć „blok iterator”:

private IEnumerable<SomeType> MyFunc(parameter a) 
{ 
    using(MyDataContext dc = new MyDataContext) 
    { 
     foreach(SomeType row in dc 
      .tablename.Where(row => row.parameter == a)) 
     { 
     yield return row; 
     } 
    } 
} 

To jest teraz odroczona bez przekazywania danych-kontekst dookoła.

+0

Próbuję zachować operacje bazy danych poza logiką biznesową, więc staram się nie ominąć datacontext. Wszystko czego chcę, to listy, przeliczalne i skalary z tych funkcji. – Spence

+0

Zobacz aktualizację, a następnie –

+0

Czy to za każdym razem trafiałoby do bazy danych? Czy też wykona zapytanie, a następnie zwróci wartość? – Spence

8

Właśnie napisali innego rozwiązania odroczonego wykonanie tego problemu here, w tym ten przykładowy kod:

IQueryable<MyType> MyFunc(string myValue) 
{ 
    return from dc in new MyDataContext().Use() 
      from row in dc.MyTable 
      where row.MyField == myValue 
      select row; 
} 

void UsingFunc() 
{ 
    var result = MyFunc("MyValue").OrderBy(row => row.SortOrder); 
    foreach(var row in result) 
    { 
     //Do something 
    } 
} 

Sposób Use() przedłużenie zasadniczo działa jak odroczony using bloku.

+0

Już zaakceptowałem odpowiedź, ale to naprawdę zmyślny sposób myślenia. Metoda rozszerzania .Use(). Zasługujesz na więcej niż +1 za taką odpowiedź. – Spence