Jaki jest cel AsQueryable ()?

107

Jest celem AsQueryable()tylko po to, abyś mógł przekazać IEnumerablemetody, które mogą się spodziewać IQueryable, lub czy istnieje przydatny powód, aby reprezentować je IEnumerablejakoIQueryable ? Na przykład, czy powinno tak być w takich przypadkach:

IEnumerable<Order> orders = orderRepo.GetAll();

// I don't want to create another method that works on IEnumerable,
// so I convert it here.
CountOrders(orders.AsQueryable());

public static int CountOrders(IQueryable<Order> ordersQuery)
{
    return ordersQuery.Count();
}

A może faktycznie sprawia, że ​​robi coś innego:

IEnumerable<Order> orders = orderRepo.GetAll();
IQueryable<Order> ordersQuery = orders.AsQueryable();

IEnumerable<Order> filteredOrders = orders.Where(o => o.CustomerId == 3);
IQueryable<Order> filteredOrdersQuery = ordersQuery.Where(o => o.CustomerId == 3);

// Are these executed in a different way?
int result1 = filteredOrders.Count();
int result2 = filteredOrdersQuery.Count();

Czy IQueryablewersje tych metod rozszerzających po prostu tworzą wyrażenie, które w końcu robi to samo po wykonaniu? Moje główne pytanie brzmi: jaki jest prawdziwy przypadek użycia AsQueryable?

Ocelot20
źródło

Odpowiedzi:

65

Istnieje kilka głównych zastosowań.

  1. Jak wspomniano w innych odpowiedziach, można go użyć do sfałszowania źródła danych, które można przeszukiwać, przy użyciu źródła danych w pamięci, dzięki czemu można łatwiej testować metody, które ostatecznie zostaną użyte na niewliczalnych bazach danych IQueryable.

  2. Możesz napisać metody pomocnika do manipulowania kolekcjami, które można zastosować do sekwencji w pamięci lub zewnętrznych źródeł danych. Jeśli napiszesz metody pomocy, aby używać ich w IQueryablecałości, możesz po prostu użyć ich AsQueryablena wszystkich wyliczeniach. Pozwala to uniknąć pisania dwóch oddzielnych wersji bardzo uogólnionych metod pomocniczych.

  3. Pozwala zmienić typ czasu kompilacji zapytania na typ IQueryable, a nie na jakiś bardziej pochodny typ. W efekcie; użyłbyś go IQueryablew tym samym czasie, co AsEnumerablena IEnumerable. Możesz mieć obiekt, który implementuje, IQueryableale ma również Selectmetodę instancji . Gdyby tak było i chcesz użyć Selectmetody LINQ , musisz zmienić typ czasu kompilacji obiektu na IQueryable. Możesz to po prostu rzucić, ale mając AsQueryablemetodę, możesz skorzystać z wnioskowania o typie. Jest to po prostu wygodniejsze, jeśli ogólna lista argumentów jest złożona i jest faktycznie konieczne, jeśli którykolwiek z ogólnych argumentów jest typem anonimowym.

Servy
źródło
Dzięki za przebudzenie. Oznaczając to jako odpowiedź, ponieważ jest najbardziej kompletna.
Ocelot20
5
Biorąc pod uwagę, że IQueryable<T>pochodzi on z, IEnumerable<T>czy możesz wyjaśnić, co masz na myśli, mówiąc o „niezliczalnym IQueryable opartym na liczbach”?
Dai
2
@Dai Odnosi się do pliku, IQueryablektóry jest czymś więcej niż tylko zapakowanym plikiem IEnumerablei w rzeczywistości wykorzystuje dodatkowe możliwości pliku IQueryable.
Servy
# 1 jest dokładnie tym, czego szukałem. Dziękuję za weryfikację.
one.beat.consumer
12

Najbardziej trafnym przypadkiem, jaki mam dla AsQueryable, jest testowanie jednostkowe. Powiedzmy, że mam następujący, nieco wymyślony przykład

public interface IWidgetRepository
{
   IQueryable<Widget> Retrieve();
} 

public class WidgetController
{
   public IWidgetRepository WidgetRepository {get; set;}


   public IQueryable<Widget> Get()
   {
      return WidgetRepository.Retrieve();
   }
}

i chcę napisać test jednostkowy, aby upewnić się, że kontroler przekazuje wyniki zwrócone z repozytorium. Wyglądałoby to mniej więcej tak:

[TestMethod]
public void VerifyRepositoryOutputIsReturned()
{    
    var widget1 = new Widget();
    var widget2 = new Widget();
    var listOfWidgets = new List<Widget>() {widget1, widget2};
    var widgetRepository = new Mock<IWidgetRepository>();
    widgetRepository.Setup(r => r.Retrieve())
      .Returns(listOfWidgets.AsQueryable());
    var controller = new WidgetController();
    controller.WidgetRepository = widgetRepository.Object;

    var results = controller.Get();

    Assert.AreEqual(2, results.Count());
    Assert.IsTrue(results.Contains(widget1));
    Assert.IsTrue(results.Contains(widget2));
}

gdzie tak naprawdę cała metoda AsQueryable () pozwala mi zadowolić kompilator podczas konfigurowania makiety.

Byłbym jednak zainteresowany, gdzie jest to używane w kodzie aplikacji.

chris
źródło
11

Jak zauważył sanjuro, cel AsQueryable () jest wyjaśniony w artykule Używanie AsQueryable z Linq do obiektów i Linq To SQL . W szczególności artykuł stwierdza:

Daje to doskonałe korzyści w rzeczywistych scenariuszach słownych, w których istnieją pewne metody na encji, które zwracają IQueryable z T i niektóre metody zwracają List. Ale masz wtedy filtr reguł biznesowych, który musi być zastosowany do całej kolekcji, niezależnie od tego, czy kolekcja jest zwracana jako IQueryable of T czy IEnumerable of T. Z punktu widzenia wydajności naprawdę chcesz wykorzystać wykonanie filtru biznesowego w bazie danych, jeśli kolekcja implementuje IQueryable w przeciwnym razie powraca do zastosowania filtru biznesowego w pamięci przy użyciu Linq do implementacji obiektów delegatów.

Scott
źródło
6

Cel AsQueryable () jest szczegółowo wyjaśniony w tym artykule Używanie AsQueryable z Linq To Objects i Linq To SQL

Z sekcji Uwagi metody MSDN Queryable.AsQueryable:

Jeśli typ źródła implementuje IQueryable, AsQueryable (IEnumerable) zwraca go bezpośrednio. W przeciwnym razie zwraca IQueryable, który wykonuje zapytania, wywołując równoważne metody operatora zapytania w Enumerable zamiast tych w Queryable.

To jest dokładnie to, co zostało wspomniane i użyte w powyższym artykule. W twoim przykładzie zależy to od tego, co zwraca orderRepo.GetAll, IEnumerable lub IQueryable (Linq to Sql). Jeśli zwróci IQueryable, metoda Count () zostanie wykonana na bazie danych, w przeciwnym razie zostanie wykonana w pamięci. Przyjrzyj się uważnie przykładowi w przywołanym artykule.

sanjuro
źródło
3

IQueryableDokumentacja dotycząca wyceny interfejsu :

Interfejs IQueryable jest przeznaczony do implementacji przez dostawców zapytań.

Tak więc dla kogoś, kto zamierza udostępnić swoją strukturę danych do zapytań w .NET, ta niepotrzebna struktura danych może zostać wyliczona lub mieć prawidłowy moduł wyliczający .

IEnumeratorjest interfejsem do iteracji i przetwarzania strumienia danych .

Tigran
źródło
Czy mógłbyś przeformułować to inaczej: „ta struktura danych, która nie jest konieczna, może być wyliczona lub mieć ważny moduł wyliczający”. Rozumiem różnicę między IQueryablei IEnumerable, ale nie rozumiem przypadku użycia AsQueryablemetody rozszerzenia. Trochę się denerwuję, jeśli chodzi o to zdanie, które, jak podejrzewam, może zawierać odpowiedź, której szukam (na podstawie upvotes).
Ocelot20
@ Ocelot20: to oznacza, że ​​struktura danych kapelusza przedstawiona przez dostawcę, który implementuje IQueryable, może nie zapewniać możliwości wyliczenia. To zależy od dostawcy. Cytowanie dokumentu: „Zapytania, które nie zwracają wyliczalnych wyników, są wykonywane po wywołaniu metody Execute”.
Tigran
Może brakuje mi czegoś oczywistego, ale nadal nie widzę odpowiedzi na pytanie „dlaczego miałbym używać AsQueryable()?” Jeśli zaczynam od IEnumerable (coś, co jest już w stanie zapewnić możliwość wyliczenia), dlaczego miałbym konwertować na IQueryableużycie metody rozszerzenia AsQueryable()? Może mały przykład kodu, aby zilustrować, gdzie to się przydaje? Wydaje mi się, że czegoś mi brakuje w twoim wyjaśnieniu. Dziękuję za Twój czas.
Ocelot20
@ Ocelot20: aby naprawdę pokazać różnicę, muszę napisać dostawcę zapytań. Wiesz, że mamy linq to sql, linq to xmli tak ... jeśli chcesz napisać linqsterownik, który jest przeznaczony dla twoich struktur danych ( linq to your data), aby użytkownicy twojego API mieli możliwość uruchamiania linqzapytań w twoich strukturach danych, musisz wybraćIQueryable
Tigran
2
Wydaje się, że tłumaczysz różnicę między IQueryablei IEnumerable. Znam różnicę i nie o to proszę. Pytam, dlaczego ktoś chciałby wziąć coś, który implementuje IEnumerablei przekształcić go w produkt IQueryableprzy użyciu AsQueryablemetody rozszerzenie. Czy to właśnie odpowiadasz? Nadal nie mogę powiedzieć ...
Ocelot20