簡體   English   中英

C# LINQ .Any 不適用於 DocumentDb CreateDocumentQuery

[英]C# LINQ .Any not working on DocumentDb CreateDocumentQuery

我正在嘗試查詢具有某種類型產品的 Art。 這是我的藝術模型:

  public string Title { get; set; }
  public string Description { get; set; }
  public List<Product> Products { get; set; }
  public string PaintedLocation { get; set; }

從這里我所做的就是以下 LINQ 查詢:

List<Art> items = DocumentDbHelper.Client.CreateDocumentQuery<Art>(collection.DocumentsLink)
                               .Where(i => i.type == "art")
                               .Where(i => i.Products.Any(p => p.Name == productType))
                               .AsEnumerable()
                               .ToList();

我收到以下錯誤:

"Method 'Any' is not supported."

我轉到代碼引用的頁面以查看支持的內容,但我沒有看到它說 Any() 不受支持,所以我可能做錯了什么。 任何幫助表示贊賞。

更新

這對我來說真的很奇怪,所以我分解了它以查看從兩個結果中返回的內容,以便更好地將問題調試為:

List<Art> items = DocumentDbHelper.Client.CreateDocumentQuery<Art>(collection.DocumentsLink)
                       .Where(i => i.Id.Contains("art"))
                       .AsEnumerable()
                       .ToList();

items = items.Where(i => i.Products.Any(p => p.Name == productType))
             .AsEnumerable()
             .ToList();

出於某種原因,這行得通,我不喜歡這個,因為因為我將它轉換為一個列表,它運行了兩次查詢 - 但它至少證明 Any() 和 Select() 在技術上應該有效。

與針對IQueryable<T> LINQ 查詢的最大混淆之一是它們看起來與針對IEnumerable<T>查詢完全相同。 好吧,前者在后者使用Func<..>時使用Expression<Func<..>> ,但除非使用顯式聲明,否則這不是那么明顯並且似乎不重要。 然而,最大的區別出現在運行時。

一旦IEnumerable<T>查詢被成功編譯,在運行時它就可以工作,而IQueryable<T>則不是這種情況。 IQueryable<T>查詢實際上是由查詢提供程序在運行時處理的表達式樹。

一方面這是一個很大的好處,另一方面,由於查詢提供程序在查詢編譯時不涉及(所有方法都由Queryable類作為擴展方法提供),因此無法知道提供程序是否支持某些構造/方法或直到運行時。 使用 Linq to Entities 的人非常了解這一點。 為了讓事情變得更難,沒有明確的文檔說明特定查詢提供程序支持什么,更重要的是,它不支持什么(正如您從提供的“支持什么”鏈接中注意到的那樣)。

解決辦法是什么? (以及為什么您的第二個代碼有效)

訣竅是針對IQueryable<T>編寫最大可能的(即由查詢提供程序支持的)查詢部分,然后切換到IEnumerable<T>並執行其余操作(記住,一旦編譯, IEnumerable<T>查詢就可以工作) . 切換由AsEnumerable()調用執行。 這就是為什么您的第二個代碼正在工作 - 因為不受支持的Any方法不再出現在 DocumentDb 查詢提供程序上下文中。 請注意,不需要調用ToList並且查詢不會執行兩次 - 實際上這種方式沒有單個查詢,而是兩個 - 一個在數據庫中,一個在內存中。

所以這樣的事情就足夠了:

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

最后,DocumentDb 查詢提供程序真正支持的是什么

從文檔中不太清楚,但答案是:完全(且僅)包含在那里的內容。 換句話說,唯一支持的查詢操作符(或者更好的說法是QueryableEnumerable擴展方法)是

  • 選擇
  • 多選
  • 在哪里
  • 訂購方式
  • 按降序排列

如您所見,它非常有限。 忘記連接和分組運算符, AnyContainsCountFirstLast等。唯一的好處是它很容易記住:)

我怎么知道? 好吧,像往常一樣,當文檔中的某些內容不清楚時,可以使用試錯法或反編譯器。 顯然在這種情況下前者不適用,所以我使用了后者。 如果您好奇,請使用您最喜歡的反編譯器並檢查Microsoft.Azure.Documents.Client.dll內部類DocumentQueryEvaluator的代碼。

我正在使用針對 .Net 4.6 的最新 Azure DocumentDB nuget。

<package id="Microsoft.Azure.DocumentDB" version="1.5.0" targetFramework="net46" />

這是對我來說很好用的示例代碼。

using System.Collections.Generic;
using System.Linq;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;

var book = client.CreateDocumentQuery<Book>(collectionLink)
                    .Where(b => b.Title == "War and Peace")
                    .Where(b => b.Publishers.Any(p => p.IsNormalized()))
                    .AsEnumerable().FirstOrDefault();
public class Book
{
    [JsonProperty("title")]
    public string Title { get; set; }

    public Author Author { get; set; }

    public int Price { get; set; }

    public List<string> Publishers { get; set; }

}

public class Author
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

您應該嘗試在此處使用IEnumerable.Contains鏈接

DbHelper.Client.CreateDocumentQuery<Art>(collection.DocumentsLink)
   .Where(i => i.type == "art")
   .Where(i => i.Products
       .Select(p => p.Name).Contains(productType))
                               .AsEnumerable()
                               .ToList();

當前最高效的解決方案是使用 SQL 語法,因為它允許文檔 DB 使用集合的索引。
例子:

SELECT a 
  FROM a
  JOIN p in a.Products
 WHERE ARRAY_CONTAINS(a.Id, 'art') 
   AND p.Name = 'My Product Type'

缺點是您可能會得到不唯一的結果,並且必須在客戶端區分結果。

要將此問題納入 DocumentDB,對以下項目進行投票會有所幫助: https : //feedback.azure.com/forums/263030-documentdb/suggestions/14829654-support-sub-query-functions-like-exists-not -存在

你為什么不試試這個?

 List<Art> items =  DocumentDbHelper.Client.CreateDocument(collection.DocumentsLink)
                           .Where(i => i.type == "art" && i.Products.Any(p => p.Name == productType))
                           .AsEnumerable()
                           .ToList();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM