Eines der größten Verwirrung mit LINQ-Abfragen gegen IQueryable<T>
ist, dass sie sehen genau das gleiche wie Abfragen für IEnumerable<T>
. Nun, der erstere verwendet Expression<Func<..>>
, wenn der spätere Func<..>
verwendet, aber außer wenn man explizite Deklarationen verwendet, ist dies nicht so auffällig und scheint unwichtig zu sein. Der große Unterschied kommt jedoch zur Laufzeit. Sobald die IEnumerable<T>
Abfrage erfolgreich kompiliert wurde, funktioniert es zur Laufzeit einfach, was bei IQueryable<T>
nicht der Fall ist. Eine IQuaryable<T>
Abfrage ist eigentlich ein Ausdrucksbaum, der zur Laufzeit vom Abfrageprovider verarbeitet wird. Auf der einen Seite ist dies ein großer Vorteil, auf der anderen Seite, da der Abfrageprovider nicht an der Kompilierungszeit der Abfrage beteiligt ist (alle Methoden werden von der Klasse Queryable
als Erweiterungsmethode bereitgestellt). Es gibt keine Möglichkeit zu wissen, ob der Provider einige unterstützt Konstruieren/Methode oder nicht bis zur Laufzeit. Leute, die Linq zu Entitäten benutzen, wissen das sehr gut. Um die Dinge schwieriger zu machen, gibt es keine klare Dokumentation, was der spezifische Abfrageanbieter unterstützt und was noch wichtiger ist, was er nicht unterstützt (wie Sie aus dem Link "Was wird unterstützt?" Erfahren haben).
Was die Lösung ist (und warum Ihr zweiter Code funktioniert)
Der Trick ist, die maximal möglichen zu schreiben (dvom Abfrageprovider unterstützt) Abfrage Teil gegen die IQueryable<T>
, und wechseln Sie dann zu IEnumerable<T>
und den Rest tun (Denken Sie daran, einmal kompiliert, IEnumerable<T>
Abfrage funktioniert einfach). Die Umschaltung erfolgt durch AsEnumerable()
Anruf. Und deshalb funktioniert Ihr zweiter Code - weil die nicht unterstützte Any
Methode nicht mehr im DocumentDb Query Provider Kontext ist. Beachten Sie, dass ToList
Aufruf nicht benötigt wird und die Abfrage nicht zweimal ausgeführt wird - tatsächlich gibt es auf diese Weise keine einzelne Abfrage, sondern zwei - eine in der Datenbank und eine im Speicher. So etwas wie dies würde ausreichen, um
List<Art> items = DocumentDbHelper.Client.CreateDocumentQuery<Art>(collection.DocumentsLink)
.Where(i => i.type == "art")
.AsEnumerable() // The context switch!
.Where(i => i.Products.Any(p => p.Name == productType))
.ToList();
Schließlich, was wirklich von der DocumentDb Abfrage Anbieter unterstützt wird
Es ist nicht ganz klar, aus der Dokumentation, aber die Antwort ist: genau (und nur) was dort enthalten ist. Mit anderen Worten, die nur unterstützt Abfrageoperatoren (oder besser sagen Queryable
oder Enumerable
Erweiterungsmethoden) sind
- Select
- Select
- Wo
- SortiertNach
- OrderByDescending
Wie Sie können sehen, es ist sehr begrenzt. Vergessen beitreten und Gruppierungsoperator, Any
, Contains
, Count
, First
, Last
usw. Die einzig Gute daran ist, dass es einfach speicherbaren :)
Wie kann ich wissen, dass? Nun, wie üblich, wenn etwas aus der Dokumentation unklar ist, verwenden Sie entweder Versuch und Irrtum oder Decompiler. Offensichtlich ist in diesem Fall der erstere nicht anwendbar, also habe ich den späteren verwendet. Wenn Sie neugierig sind, verwenden Sie Ihren bevorzugten Decompiler und überprüfen Sie den Code der internen Klasse DocumentQueryEvaluator
innerhalb der Microsoft.Azure.Documents.Client.dll
.
Hallo, sind Sie in der Lage hier, um die Details Fehler zu setzen? – juvchan
Ich habe es hinzugefügt, aber es wird nicht viel mehr dazu hinzufügen. Die Quelle des Fehlers ist Microsoft.Azure.Documents.Client – gregwhitworth
Wird in der Dokumentation angegeben, dass es unterstützt wird? Ich glaube nicht, dass Sie davon ausgehen sollten, dass es nur deshalb ist, weil sie nicht explizit erwähnt haben, dass es nicht so ist. – MarcinJuraszek