Was ist der Zweck von AsQueryable ()?

106

Ist der Zweck AsQueryable()nur so, dass Sie eine IEnumerableMethode weitergeben können, die Sie vielleicht erwarten IQueryable, oder gibt es einen nützlichen Grund, IEnumerableals darzustellen IQueryable? Zum Beispiel soll es für Fälle wie diesen sein:

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();
}

Oder macht es tatsächlich etwas anderes:

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();

IQueryableBauen die Versionen dieser Erweiterungsmethoden nur einen Ausdruck auf, der nach seiner Ausführung dasselbe tut? Meine Hauptfrage ist, was ist ein realer Anwendungsfall für die Verwendung AsQueryable?

Ocelot20
quelle

Antworten:

65

Es gibt einige Hauptanwendungen.

  1. Wie in anderen Antworten erwähnt, können Sie damit eine abfragbare Datenquelle mithilfe einer speicherinternen Datenquelle verspotten, um Methoden, die möglicherweise auf einer nicht aufzählbaren Basis verwendet werden, einfacher zu testen IQueryable.

  2. Sie können Hilfsmethoden zum Bearbeiten von Sammlungen schreiben, die entweder auf speicherinterne Sequenzen oder auf externe Datenquellen angewendet werden können. Wenn Sie Ihre Hilfemethoden IQueryablevollständig schreiben , können Sie sie einfach AsQueryablefür alle Enumerables verwenden, um sie zu verwenden. Auf diese Weise können Sie vermeiden, zwei separate Versionen sehr allgemeiner Hilfsmethoden zu schreiben.

  3. Sie können den Kompilierungszeittyp eines abfragbaren Typs so ändern, dass er ein IQueryableabgeleiteter Typ ist. In der Tat; Sie würden es IQueryablegleichzeitig AsEnumerablemit einem verwenden IEnumerable. Möglicherweise haben Sie ein Objekt, das implementiert wird, IQueryableaber auch eine Instanzmethode Select. Wenn dies der Fall wäre und Sie die LINQ- SelectMethode verwenden möchten , müssen Sie den Kompilierungszeittyp des Objekts in ändern IQueryable. Sie könnten es einfach umwandeln, aber mit einer AsQueryableMethode können Sie die Typinferenz nutzen. Dies ist einfach praktischer, wenn die Liste der generischen Argumente komplex ist, und es ist tatsächlich erforderlich, wenn eines der generischen Argumente anonyme Typen sind.

Servieren
quelle
Danke für die Wiederbelebung. Markieren Sie dies als Antwort, da es die vollständigste ist.
Ocelot20
5
Da IQueryable<T>ergibt sich aus IEnumerable<T>können Sie mir bitte erklären , was Sie unter „nicht-zählbare basierend IQueryable“?
Dai
2
@Dai Es bezieht sich auf ein IQueryable, das mehr als nur ein Wrapped ist IEnumerableund das tatsächlich die zusätzlichen Fähigkeiten eines nutzt IQueryable.
Servy
# 1 ist genau das, wonach ich gesucht habe. Vielen Dank für die Überprüfung.
one.beat.consumer
12

Der gültigste Fall für AsQueryable ist das Testen von Einheiten. Angenommen, ich habe das folgende etwas erfundene Beispiel

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

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


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

und ich möchte einen Komponententest schreiben, um sicherzustellen, dass der Controller die vom Repository zurückgegebenen Ergebnisse zurückgibt. Es würde ungefähr so ​​aussehen:

[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));
}

Wo wirklich alles, was die AsQueryable () -Methode mir erlaubt, ist, den Compiler beim Einrichten eines Mocks zufrieden zu stellen.

Mich würde interessieren, wo dies im Anwendungscode verwendet wird.

chris
quelle
11

Wie sanjuro bemerkte, wird der Zweck von AsQueryable () unter Verwenden von AsQueryable mit Linq To Objects und Linq To SQL erläutert . Insbesondere heißt es in dem Artikel:

Dies bietet hervorragende Vorteile in realen Wortszenarien, in denen bestimmte Methoden für eine Entität vorhanden sind, die eine IQueryable von T zurückgeben, und einige Methoden eine Liste zurückgeben. Dann haben Sie jedoch einen Geschäftsregelfilter, der auf die gesamte Sammlung angewendet werden muss, unabhängig davon, ob die Sammlung als IQueryable von T oder IEnumerable von T zurückgegeben wird. Unter Leistungsgesichtspunkten möchten Sie die Ausführung des Geschäftsfilters in der Datenbank wirklich nutzen, wenn Die Auflistung implementiert IQueryable. Andernfalls wird zurückgegriffen, um den Geschäftsfilter im Speicher mithilfe von Linq auf die Objektimplementierung von Delegaten anzuwenden.

Scott
quelle
6

Der Zweck von AsQueryable () wird in diesem Artikel ausführlich erläutert. Verwenden von AsQueryable mit Linq To Objects und Linq To SQL

Aus dem Abschnitt "Bemerkungen" der MSDN Queryable.AsQueryable-Methode:

Wenn der Quellentyp IQueryable implementiert, gibt AsQueryable (IEnumerable) ihn direkt zurück. Andernfalls wird eine IQueryable zurückgegeben, die Abfragen ausführt, indem die entsprechenden Abfrageoperatormethoden in Enumerable anstelle der in Queryable aufgerufen werden.

Das ist genau das, was im obigen Artikel erwähnt und verwendet wird. In Ihrem Beispiel hängt es davon ab, was orderRepo.GetAll zurückgibt, IEnumerable oder IQueryable (Linq to Sql). Wenn IQueryable zurückgegeben wird, wird die Count () -Methode in der Datenbank ausgeführt, andernfalls wird sie im Speicher ausgeführt. Schauen Sie sich das Beispiel im Artikel an, auf den verwiesen wird.

Sanjuro
quelle
3

IQueryableDokumentation zu Schnittstellenzitaten :

Die IQueryable-Schnittstelle ist für die Implementierung durch Abfrageanbieter vorgesehen.

Für jemanden, der beabsichtigt, seine Datenstruktur in .NET abfragbar zu machen, kann die nicht erforderliche Datenstruktur aufgelistet werden oder einen gültigen Enumerator haben .

IEnumeratorist eine Schnittstelle zum Iterieren und Verarbeiten von Datenströmen .

Tigran
quelle
Würde es Ihnen etwas ausmachen, dies neu zu formulieren: "Diese nicht notwendige Datenstruktur kann aufgelistet werden oder einen gültigen Enumerator haben". Ich verstehe den Unterschied zwischen IQueryableund IEnumerable, aber ich verstehe den Anwendungsfall für die AsQueryableErweiterungsmethode nicht. Ich bin jedoch ein bisschen über diesen Satz gestolpert, von dem ich vermute, dass er die Antwort hat, nach der ich suche (basierend auf den positiven Stimmen).
Ocelot20
@ Ocelot20: Dies bedeutet, dass die vom Anbieter, der IQueryable implementiert, präsentierte Datenstruktur möglicherweise keine Aufzählungsfähigkeit bietet. Es liegt am Anbieter. Zitat doc: "Abfragen, die keine aufzählbaren Ergebnisse zurückgeben, werden ausgeführt, wenn die Execute-Methode aufgerufen wird."
Tigran
Vielleicht fehlt mir etwas Offensichtliches, aber ich sehe immer noch keine Antwort auf "Warum sollte ich verwenden müssen AsQueryable()?" Wenn ich beginne mit einem IEnumerable (etwas schon der Lage ist, eine Aufzählung Fähigkeit), warum sollte ich zu konvertieren muß IQueryabledie Erweiterungsmethode verwenden AsQueryable()? Vielleicht ein kleines Codebeispiel, um zu veranschaulichen, wo dies nützlich wäre? Mir scheint etwas in Ihrer Erklärung zu fehlen. Vielen Dank für Ihre Zeit.
Ocelot20
@ Ocelot20: Um den Unterschied wirklich zu zeigen, muss ich einen Abfrageanbieter schreiben. Sie wissen, dass wir haben linq to sql, linq to xmlund so ... Wenn Sie einen linqTreiber schreiben möchten , der auf Ihre Datenstrukturen ( linq to your data) abzielt , damit Benutzer Ihrer API die Möglichkeit haben, linqAbfragen für Ihre Datenstrukturen auszuführen , müssen Sie wählenIQueryable
Tigran
2
Sie scheinen den Unterschied zwischen IQueryableund zu erklären IEnumerable. Ich kenne den Unterschied und das ist nicht das, wonach ich frage. Ich frage, warum jemand etwas nehmen möchte, das implementiert wird IEnumerable, und es IQueryablemithilfe der AsQueryableErweiterungsmethode in eine konvertieren möchte . Beantworten Sie das? Ich kann es immer noch nicht sagen ..
Ocelot20