C#LINQ.没有处理DocumentDb CreateDocumentQuery

gre*_*rth 26 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; }
Run Code Online (Sandbox Code Playgroud)

从这里我所做的就是以下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();
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

"Method 'Any' is not supported."
Run Code Online (Sandbox Code Playgroud)

我去了代码引用的页面,看看支持什么,但我没有看到它说不支持Any(),所以我可能做错了.任何帮助表示赞赏.

UPDATE

这对我来说真的很奇怪,所以我把它拆开来看看从两个结果中返回的是什么来更好地调试问题:

        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();
Run Code Online (Sandbox Code Playgroud)

由于某些原因,这不起作用,因为我将它转换为列表,因此我正在运行查询两次 - 但至少证明Any()和Select()在技术上应该有效.

Iva*_*oev 76

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

解决方案是什么(为什么你的第二个代码有效)

诀窍是写入最大可能(由查询提供程序支持)查询部分IQueryable<T>,然后切换到IEnumerable<T>并执行其余操作(记住,一旦编译,IEnumerable<T>查询就可以工作).通过AsEnumerable()呼叫执行切换.这就是你的第二个代码工作的原因 - 因为AnyDocumentDb查询提供程序上下文中不再支持不受支持的方法.请注意,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();
Run Code Online (Sandbox Code Playgroud)

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

从文档中可以清楚地看出,但答案是:确切地(并且仅限于)那里包含的内容.换句话说,唯一支持的查询运算符(或更好的说法QueryableEnumerable扩展方法)是

  • 选择
  • 的SelectMany
  • 哪里
  • 排序依据
  • OrderByDescending

正如您所看到的,它非常有限.忘了加入和分组运算符,Any,Contains,Count,First,Last等等.唯一的好处是,它很容易memorizable :)

我怎么知道的?好吧,像往常一样,当文档中的某些内容不清楚时,可以使用试错法或反编译器.显然在这种情况下前者不适用,所以我使用了后者.如果你是好奇,用你喜欢的反编译和检查内部类的代码DocumentQueryEvaluator里面Microsoft.Azure.Documents.Client.dll.

  • 只想添加,请注意DocumentDB中支持一些新的LINQ运算符,包括Math,String,Spatial和Array运算符(包括Contains).详情请访问:https://azure.microsoft.com/en-us/blog/azure-documentdb-s-linq-provider-just-got-better/ (8认同)
  • 非常感谢你 - 这确实是问题所在,我非常感谢你不仅提供解决方案,而且提供了背后的理由,"教导一个人钓鱼......"等等.谢谢!! (3认同)
  • 类似的事情发生在我对真实环境使用单元测试.单元测试使用IEnumerable <>来模拟上下文以保存数据.Any()用于单元测试.发布它测试和繁荣:例外. (2认同)

juv*_*han 5

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

<package id="Microsoft.Azure.DocumentDB" version="1.5.0" targetFramework="net46" />
Run Code Online (Sandbox Code Playgroud)

这是示例代码,对我来说很好.

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; }
Run Code Online (Sandbox Code Playgroud)

}

public class Author
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)