我试图查询具有某种类型产品的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()在技术上应该有效。
答案 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
扩展方法)
正如您所看到的,它非常有限。忘记加入和分组运算符,Enumerable
,Any
,Contains
,Count
,First
等。唯一的好处是它很容易记忆:) / 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();