2013-07-03 19 views
7

Czy efQuery.ToList().Count i efQuery.Count() mają tę samą wartość? Zliczanie EntityFramework wyników zapytania z listą zliczania

Jak to jest możliwe, że efQuery.ToList().Count i nie dają tej samej wartości?

//GetQuery() returns a default IDbSet which is used in EntityFramework 

using (var ds = _provider.DataSource()) 
{ 
    //return GetQuery(ds, filters).Count(); //returns 0??? 
    return GetQuery(ds, filters).ToList().Count; //returns 605 which is correct based on filters 
} 

+1

Czy efQuery IEnumerable lub IQueryable? Także, jeśli mógłbyś opublikować swój rzeczywisty kod, który mógłby pomóc. – Maess

+0

efQuery to 'IQueryable', jest to zapytanie, które nie zostało jeszcze wykonane na bazie danych. Dodałem kod. – ReFocus

Odpowiedz

1

Zakładając tu, że efQuery jest IQueryable:

ToList() faktycznie wykonuje zapytanie. Jeśli zmiany danych w magazynie danych, między połączeniami do ToList() i .Count(), spowoduje to inny zestaw wyników, wywołanie ToList() spowoduje ponowne wypełnienie listy. ToList().Count i .Count() należy następnie dopasowywać, aż dane w sklepie ponownie zmienią zestaw wyników.

+1

Dane nie zmieniają się między połączeniami. Mogę odtworzyć ten problem za każdym razem, gdy uruchamiam kod. Oba połączenia dają różne wyniki. Nawet Resharper mówi mi, że powinienem użyć metody '.Count()' zamiast forsować '.ToList()' i używając '.Count' property ... – ReFocus

+1

Count() zwraca 0, ponieważ lista nie jest jeszcze wypełniona . Musisz wykonać kwerendę za pomocą ToList() przed wykonaniem kwerendy. Jeśli dodasz.Count() (bez ToList()) po linii ToList(), liczba będzie poprawna. –

+2

Nie, 'Count()' powinno wymusić zapytanie na 'IQueryable' - http://stackoverflow.com/questions/890381/how-to-countrow-within-entityframework-withoutloading-contents – ReFocus

5

Po prostu wpadłem na to sam. W moim przypadku problem polega na tym, że zapytanie ma klauzulę .Select(), która powoduje, że tworzone są dalsze relacje, co w efekcie powoduje dalsze filtrowanie zapytania, ponieważ powiązanie wewnętrzne związku ogranicza wynik.

Wygląda na to, że .Count() nie przetwarza części .Select() zapytania.

Więc mam:

// projection created 
var ordersData = orders.Select(ord => new OrderData() { 
      OrderId = ord.OrderId, 
      ... more simple 1 - 1 order maps 

      // Related values that cause relations in SQL 
      TotalItemsCost = ord.OrderLines.Sum(lin => lin.Qty*lin.Price), 
      CustomerName = ord.Customer.Name, 
}; 


var count = ordersData.Count(); // 207 
var count = ordersData.ToList().Count // 192 

Kiedy porównać SQL Uważam, że count() robi to bardzo prosta suma na tabeli Zamówienia, która zwraca wszystkie rozkazy, natomiast drugie zapytanie jest potworem 100 + linie SQL, które mają 10 wewnętrznych sprzężeń, które są wyzwalane przez klauzulę .Select() (istnieje kilka więcej powiązanych wartości/agregacji pobranych niż pokazano tutaj).

Zasadniczo wydaje się wskazywać, że .Count() nie ponosi .Wybrać() Klauzula pod uwagę, gdy robi swój licznik, więc te same związki, które powodują dalsze ograniczające zestawu wynikowego nie są wypalane na .Count().

byłem w stanie wykonać tej pracy przez jawnie dodawanie wyrażeń metody .Count(), która ciągnąć w niektórych z tych zagregowanych wartości wynikowych, które skutecznie zmuszają je do .Count(), a także zapytania:

var count = ordersData.Count(o=> o.TotalItemsCost != -999 && 
            o.Customer.Name != "[email protected]#"); // 207 

Kluczem jest upewnienie się, że którekolwiek z pól, które są obliczane lub przyciągane do powiązanych danych i wywołują związek, są uwzględniane w wyrażeniu, które wymusza w Count() uwzględnienie wymaganych relacji w zapytaniu.

Zdaję sobie sprawę, że to total hack i mam nadzieję, że jest lepszy sposób, ale na chwilę to pozwoliło nam przynajmniej uzyskać odpowiednią wartość bez ciągnięcia ogromnych danych za pomocą .ToList().

Powiązane problemy