C#LINQ。没有处理DocumentDb CreateDocumentQuery

时间:2015-11-21 04:52:36

标签: c# linq azure azure-cosmosdb

我试图查询具有某种类型产品的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."

我转到了代码引用的页面,看到了what is 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()在技术上应该有效。

5 个答案:

答案 0 :(得分:72)

IQueryable<T>的LINQ查询最大的混淆之一是它们看起来与针对IEnumerable<T>的查询完全相同。好吧,前者使用Expression<Func<..>>只要后者使用Func<..>,但除非使用显式声明,否则这不太明显,似乎并不重要。但是,最大的不同在于运行时。成功编译IEnumerable<T>查询后,在运行时它就可以正常工作,而IQueryable<T>则不然。 IQuaryable<T>查询实际上是一个表达式树,由查询提供程序在运行时处理。从一方面来看,这是一个很大的好处,从另一方面来说,因为查询提供程序不参与查询编译时(所有方法都是由Queryable类提供的扩展方法),所以无法知道是否提供者支持一些构造/方法,直到运行时。使用Linq to Entities的人非常清楚。为了使事情更难,没有明确的文档,具体的查询提供程序支持什么,更重要的是,它不支持(正如你从你提供的“支持什么”链接中注意到的)。

解决方案是什么(以及您的第二个代码的工作原理)

诀窍是针对IQueryable<T>编写最大可能(由查询提供程序支持)查询部分,然后切换到IEnumerable<T>并完成剩下的工作(记住,编译后,{{ 1}}查询正常工作)。切换由IEnumerable<T>呼叫执行。这就是你的第二个代码工作的原因 - 因为DocumentDb查询提供程序上下文中不再支持AsEnumerable()方法。请注意,不需要Any调用,并且查询不会执行两次 - 实际上这种方式没有单个查询,而是两个 - 一个在数据库中,一个在内存中。所以这样的事情就足够了

ToList

最后,DocumentDb查询提供程序真正支持的内容

文档中不太清楚,但答案是:完全(且仅限)包含的内容。换句话说,唯一支持的查询运算符(或更好的说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(); Queryable扩展方法)

  • 选择
  • 的SelectMany
  • 其中
  • 排序依据
  • OrderByDescending

正如您所看到的,它非常有限。忘记加入和分组运算符,EnumerableAnyContainsCountFirst等。唯一的好处是它很容易记忆:) / p>

我怎么知道?好吧,像往常一样,当文档中的某些内容不清楚时,可以使用试错法或反编译器。显然在这种情况下前者不适用,所以我使用了后者。如果您好奇,请使用您最喜欢的反编译器并检查Last内部类DocumentQueryEvaluator的代码。

答案 1 :(得分:5)

我正在使用最新的Azure DocumentDB nuget目标.Net 4.6。

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

答案 2 :(得分:2)

您应该尝试使用IEnumerable.Contains link here

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

答案 3 :(得分:1)

目前最高效的解决方案是使用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-exist

答案 4 :(得分:0)

为什么不试试这个?

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