2013-03-08 8 views
15

Wyobrazić sobie klasę, powiedzmy, do stronicowania, które mogą być używane z IList<T> lub IQueryable<T>.Użyj IQueryable.Count <T> z IEnumerable <T> parametr

Ta klasa miałaby właściwość int TotalItems, co mogłoby (nie jest to zaskakujące) uzyskać/ustawić liczbę parametru możliwego do wyśledzenia lub przeliczenia.

Jeśli używam IEnumerable<T> jako parametr,

//simplified 
public Pagination(IEnumerable<T> query) 
    { 
     TotalItems = query.Count(); 
    } 

metoda Count() będzie (jeśli się nie mylę, to punkt) Enumerable.Count(). Więc nawet jeśli zapytanie to IQueryable<T> (które dziedziczy po IEnumerable<T>), zostanie ono wyliczone (co oczywiście nie jest pożądane z "zapytaniem db").

Więc jest jakiś sposób na wykorzystanie Queryable.Count() metodę gdy mój IEnumerable<T> jest w rzeczywistości IQueryable<T>, czy muszę zmienić mój projekt, mający na przykład w tym przypadku 2 konstruktor

//simplified 
public Pagination(IEnumerable<T> query) 
    { 
     TotalItems = query.Count(); 
    } 
public Pagination(IQueryable<T> query) 
    { 
     TotalItems = query.Count(); 
    } 

EDIT Rozumiem, że IQueryable<T> dziedziczenie z IEnumerable<T> nie ma nic wspólnego z tym, że IEnumerable<T> imają metody rozszerzenia o tej samej nazwie i że dobrze jest mieć takie same nazwy dla metody rozszerzenia, które "wyglądają tak, jak robią to samo", ale myślę, że to nadal czasami mylące ...

Generic pytanie z ciekawości

Czy są inne przykłady, w ramach, z tym samym „architektura”: dziedziczenie + nazwy zwyczajowe dla metod rozszerzenie?

+0

dlaczego po prostu nie akceptować "int" i pozwolić im zliczyć sekwencję/zapytanie, skąd to się wzięło? – Servy

+0

@AakashM cóż, to pytanie pojawia się po prawdziwej próbie i spojrzeniu na wyniki MiniProfilera: być może zrobiłem złą interpretację, w takim przypadku rozbiję głowę na ścianie;) –

+0

@Servy jak wskazano, to jest przypadkiem uproszczonym, mógłbym chcieć użyć parametru 'query' w innym celu w mojej klasie i wywołać inne" udostępnione "zapytania lub możliwe do rozszerzenia metody rozszerzenia, które miałyby ten sam problem (jeśli problem istnieje). –

Odpowiedz

8

Powinieneś mieć dwa przeciążenia konstruktora, jak pokazałeś w pytaniu. To sprawia, że ​​do rozmówcy zależy, czy chcą zastosować metody, czy zastosować metody .

Gdyby ktoś zrobić:

Pagination pager = new Pagination(query.AsEnumerable()); 

Potem wyraźnie obiekt ma być przetwarzany jako IEnumearble, a nie IQueryable. Być może wiedzą, że ich dostawca zapytań nie implementuje Skip i Take, a więc paginacja się nie powiedzie i musi być oceniona jako Linq-do-obiektów.

Dzięki dwóm przeciążeniom podejmujesz świadomą decyzję użytkownika swojej klasy, czy ma do czynienia z sekwencją w pamięci lub zapytaniem, zamiast próbować samodzielnie go rozgryźć.

+0

+1, dobry przykład przy Pomijanie/Przyjmowanie, który może nie być obsługiwany przez dostawcę zapytań. – ken2k

+0

+1 także, dobry przykład nie implementowanych metod rozszerzeń u różnych dostawców. Wkrótce przyjęto, tylko pytanie: nie sądzisz, że mógłbym wtedy mieć parametr z IEnumerable parametr zastąpiony przez parametr IList : wiedząc, że Count() wyliczy tak czy inaczej, to sprawi, że rzeczy będą wyraźniejsze, nie? –

1

Cóż, można zrobić:

TotalItems = enumerable.AsQueryable().Count(); 

To bezpośrednio korzystać Count realizację zapytaniu świadczący dla natywnego queryable lub awaryjne do LINQ to Objects (z pewnym narzutem za korzystanie z EnumerableQuery) inaczej.

Innym rozwiązaniem (potencjalnie bardziej wydajne):

var queryable = enumerable as IQueryable<T>; 
TotalItems = queryable != null ? queryable.Count() : enumerable.Count(); 

Należy jednak pamiętać, że jeśli queryable realizuje ICollection lub ICollection<T> skutecznie (jak ma to miejsce w przypadku niektórych operatorów zapytań), nawet istniejące rozwiązanie byłoby potencjalnie działa dobrze z powodu optymalizacji w LINQ (w miarę możliwości używa właściwości Count z tych interfejsów). W przypadku Twojego IList<T> zawsze będzie korzystać z tej optymalizacji, ponieważ implementuje ona ICollection<T>.

+3

Może to jednak powodować problemy. Jeśli ktoś ma 'IQueryable' i wywołuje na nim' AsEnumerable', oznacza to, że * chcą *, aby został rozwiązany jako LINQ-do-obiektów, i nie wykonuje kolejnych zapytań przeciwko dostawcy zapytań. – Servy

+0

@Servy cóż, czy rzeczywiście byłby wykonywany (ponownie) w stosunku do dostawcy zapytań, ponieważ zostałby wyliczony za pomocą 'AsEnumerable()'? Muszę przyznać, że nie jestem do końca jasny na temat tego, co się dzieje z 'IQueryable () .AsEnumerable(). AsQueryable()' ... –

+1

@ RaphaëlAlthaus Nie, 'AsEnumerable' nie wylicza niczego, to faktycznie nie działa cokolwiek więcej niż rzucić obiekt na "IEnumerable". Pełna definicja: 'public static IEnumerable AsEnumerable (to źródło IEnumerable ) {return source;}' – Servy

Powiązane problemy