2012-11-29 15 views
28

Jestem nowy w EF i próbuję użyć metody rozszerzenia, która konwertuje z mojej bazy danych typu User do mojej klasy informacji UserInfo.
Najpierw używam bazy danych, jeśli to robi różnicę? poniżejOperacja nie może zostać zakończona, ponieważ DbContext został usunięty błąd

Mój kod daje błąd

Operacja nie może być zakończona, ponieważ DbContext zostało usunięte.

try 
{ 
    IQueryable<User> users; 
    using (var dataContext = new dataContext()) 
    { 
     users = dataContext.Users 
        .Where(x => x.AccountID == accountId && x.IsAdmin == false); 
     if(users.Any() == false) 
     { 
      return null; 
     } 
    } 
    return users.Select(x => x.ToInfo()).ToList(); // this line is the problem 
} 
catch (Exception ex) 
{ 
    //... 
} 

widzę, dlaczego byłoby to zrobić, ale ja też nie rozumiem, dlaczego wynik z którym oświadczenie nie jest zapisywane do obiektu users?

Zgaduję więc, że moje główne pytanie brzmi: dlaczego to nie działa, a po drugie, jaki jest właściwy sposób stosowania metod rozszerzających i EF?

Odpowiedz

29

To question & answer doprowadziło mnie do przekonania, że ​​produkt IQueryable wymaga aktywnego kontekstu dla jego działania. Oznacza to, że należy spróbować to zamiast:

try 
{ 
    IQueryable<User> users; 

    using (var dataContext = new dataContext()) 
    { 
     users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false); 

     if(users.Any() == false) 
     { 
      return null; 
     } 
     else 
     { 
      return users.Select(x => x.ToInfo()).ToList(); // this line is the problem 
     } 
    } 


} 
catch (Exception ex) 
{ 
    ... 
} 
+1

Dzięki. Teraz widzę, że IQueryable to miejsce, w którym wpadam w kłopoty. Jeśli wstawię użytkowników. Wyselekcjonowanie() ... do instrukcji using daje mi kolejny błąd (chyba inne pytanie). Więc zmieniłem typ zwracania na IEnumerable i to rozwiązało mój problem. – Colin

+0

Dziękuję, że to działa dla mnie i teraz dowiedziałem się, że IQueryable wymagany aktywny DBContext –

2

Powodem jest rzucanie błąd jest przedmiotem jest umieszczona, a potem staramy się uzyskać dostęp do wartości tabeli przez obiekt, ale obiekt jest disposed.Better do przekonwertuj to na ToList(), abyśmy mogli mieć wartości

Może to nie jest faktycznie pobieranie danych do momentu użycia (jest to leniwe ładowanie), więc dataContext nie istnieje, gdy próbujesz wykonać pracę . Założę się, że gdybyś zrobił ToList() w zakresie, byłoby dobrze.

try 
{ 
    IQueryable<User> users; 
    var ret = null; 

    using (var dataContext = new dataContext()) 
    { 
     users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false); 

     if(users.Any()) 
     { 
      ret = users.Select(x => x.ToInfo()).ToList(); 
     } 

    } 

    Return ret; 
} 
catch (Exception ex) 
{ 
    ... 
} 
22

Przedmioty wystawione jako IQueryable<T> i IEnumerable<T> rzeczywistości nie „execute” dopóki są one powtórzyć się lub w inny sposób dostępne, jak jest złożona w List<T>. Kiedy EF zwraca wartość IQueryable<T>, to zasadniczo tworzy coś, co jest zdolne do pobierania danych, ale nie wykonuje tego, dopóki nie zostanie zużyty.

Możesz poczuć to, umieszczając punkt przerwania, w którym zdefiniowano IQueryable, a kiedy zostanie wywołane .ToList(). (Z wewnątrz kontekstu danych, jak słusznie wskazał Jofry.) Praca polegająca na pobieraniu danych odbywa się podczas wywołania ToList().

Z tego powodu należy zachować IQueryable<T> w kontekście kontekstu danych.

12

Należy pamiętać, że kwerendy IQueryable nie są faktycznie wykonywane względem składnicy danych, dopóki nie zostaną wyliczone.

using (var dataContext = new dataContext()) 
{ 

ta linia kodu w rzeczywistości nie robić nic innego niż zbudować SQL

users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false); 

.Any() to operacja, która wylicza IQueryable, więc SQL jest wysyłane do danych source (przez dataContext), a następnie.Jakiekolwiek() operacje wykonywane jest przeciwko niemu

if(users.Any() == false) 
    { 
     return null; 
    } 
} 

Twój wiersz „problem” jest ponowne sql zbudowany powyżej, a następnie robi dodatkową operację (.Wybrać()), który po prostu dodaje się do zapytania. Jeśli zostawiłeś go tutaj, bez wyjątku, z wyjątkiem linii problemu

return users.Select(x => x.ToInfo()).ToList(); // this line is the problem 

nazywa .ToList(), która wylicza IQueryable, co powoduje SQL do wysłania do źródła danych poprzez DataContext, który został użyty w oryginale Zapytanie LINQ. Ponieważ ta dataContext została usunięta, nie jest już poprawna, a .ToList() zgłasza wyjątek.

To jest "dlaczego to nie działa". Poprawka polega na przeniesieniu tego wiersza kodu do zakresu twoich danych.

Sposób poprawnego używania jest kolejnym pytaniem z kilkoma prawdopodobnie poprawnymi odpowiedziami, które zależą od twojej aplikacji (Forms vs. ASP.net vs. MVC, itp.). Wzór, który to implementuje, jest wzorem jednostki pracy. Utworzenie nowego obiektu kontekstowego nie ma prawie żadnych kosztów, więc ogólną zasadą jest utworzenie go, wykonanie pracy, a następnie pozbycie się go. W aplikacjach internetowych niektóre osoby utworzą Kontekst na każde żądanie.

+0

SingleOrDefault() również powoduje podobny problem – code4j

0

Może to być tak proste, jak dodanie ToList() do repozytorium. Na przykład:

public IEnumerable<MyObject> GetMyObjectsForId(string id) 
{ 
    using (var ctxt = new RcContext()) 
    { 
     // causes an error 
     return ctxt.MyObjects.Where(x => x.MyObjects.Id == id); 
    } 
} 

przyniesie Context Db wyrzucać błąd w klasie wywołującej ale ten może być rozwiązany poprzez wyraźne wykonywania wyliczanie dodając ToList() na działania LINQ:

public IEnumerable<MyObject> GetMyObjectsForId(string id) 
{ 
    using (var ctxt = new RcContext()) 
    { 
     return ctxt.MyObjects.Where(x => x.MyObjects.Id == id).ToList(); 
    } 
} 
1

to zmienić :

using (var dataContext = new dataContext()) 
{ 
    users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false); 

    if(users.Any()) 
    { 
     ret = users.Select(x => x.ToInfo()).ToList(); 
    } 

} 

do tego:

using (var dataContext = new dataContext()) 
{ 
    return = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false).Select(x => x.ToInfo()).ToList(); 
} 

Istotą jest to, że chcesz raz wymusić wyliczenie zestawu danych kontekstu. Niech rozmówca poradzi sobie z pustym scenariuszem, tak jak powinni.

1

Tutaj próbujesz wykonać obiekt IQueryable na nieaktywnym DBContext. Twój tekst kontekstowy jest już usunięty. możesz tylko wykonać obiekt IQueryable przed usunięciem DBContext. Oznacza, że ​​musisz napisać oświadczenie users.Select(x => x.ToInfo()).ToList() wewnątrz używając zakresu

Powiązane problemy