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."
我转到了代码引用的页面以查看 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() 在技术上应该可行。
我正在使用针对 .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
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();
针对 IQueryable<T>
的 LINQ 查询最大的混淆之一是它们看起来与针对 IEnumerable<T>
的查询完全相同。好吧,前者在后者使用 Func<..>
时使用 Expression<Func<..>>
,但除非有人使用显式声明,否则这不是那么引人注目并且似乎不重要。然而,最大的不同在于运行时。
一旦 IEnumerable<T>
查询被成功编译,在运行时它就可以正常工作,而 IQueryable<T>
则不是这样。 IQueryable<T>
查询实际上是一个表达式树,由查询提供程序在运行时处理。
一方面,这是一个很大的好处,另一方面,因为在查询编译时不涉及查询提供程序(所有方法都作为扩展方法由 Queryable
class ),在运行时之前无法知道提供者是否支持某些 construct/method。使用 Linq to Entities 的人非常了解这一点。使事情变得更难的是,没有明确的文档说明特定查询提供程序支持什么,更重要的是,它不支持什么(正如您从提供的“支持的内容”link 中注意到的那样)。
有什么解决办法? (以及为什么你的第二个代码有效)
诀窍是针对 IQueryable<T>
编写最大可能的(查询提供者 i.e.supported)查询部分,然后切换到 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 查询提供程序真正支持什么
文档中的内容不是很清楚,但答案是:完全(且仅)包含在那里的内容。换句话说,唯一受支持的查询运算符(或者更好地说 Queryable
或 Enumerable
扩展方法)是
- Select
- Select很多
- 在哪里
- 排序方式
- 降序排列
如您所见,它非常有限。忘掉连接和分组运算符,Any
、Contains
、Count
、First
、Last
等。唯一的好处是它很容易记住 :)
我怎么知道的?好吧,像往常一样,当文档中的内容不清楚时,要么使用试错法,要么使用反编译器。显然在这种情况下前者不适用,所以我使用了后者。如果你好奇,使用你最喜欢的反编译器并检查Microsoft.Azure.Documents.Client.dll
.
内部classDocumentQueryEvaluator
的代码
你为什么不试试这个?
List<Art> items = DocumentDbHelper.Client.CreateDocument(collection.DocumentsLink)
.Where(i => i.type == "art" && i.Products.Any(p => p.Name == productType))
.AsEnumerable()
.ToList();
目前最高效的解决方案是使用 SQL 语法,因为它允许文档数据库使用集合的索引。
例子:
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
我正在尝试查询具有特定类型产品的 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() 在技术上应该可行。
我正在使用针对 .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
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();
针对 IQueryable<T>
的 LINQ 查询最大的混淆之一是它们看起来与针对 IEnumerable<T>
的查询完全相同。好吧,前者在后者使用 Func<..>
时使用 Expression<Func<..>>
,但除非有人使用显式声明,否则这不是那么引人注目并且似乎不重要。然而,最大的不同在于运行时。
一旦 IEnumerable<T>
查询被成功编译,在运行时它就可以正常工作,而 IQueryable<T>
则不是这样。 IQueryable<T>
查询实际上是一个表达式树,由查询提供程序在运行时处理。
一方面,这是一个很大的好处,另一方面,因为在查询编译时不涉及查询提供程序(所有方法都作为扩展方法由 Queryable
class ),在运行时之前无法知道提供者是否支持某些 construct/method。使用 Linq to Entities 的人非常了解这一点。使事情变得更难的是,没有明确的文档说明特定查询提供程序支持什么,更重要的是,它不支持什么(正如您从提供的“支持的内容”link 中注意到的那样)。
有什么解决办法? (以及为什么你的第二个代码有效)
诀窍是针对 IQueryable<T>
编写最大可能的(查询提供者 i.e.supported)查询部分,然后切换到 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 查询提供程序真正支持什么
文档中的内容不是很清楚,但答案是:完全(且仅)包含在那里的内容。换句话说,唯一受支持的查询运算符(或者更好地说 Queryable
或 Enumerable
扩展方法)是
- Select
- Select很多
- 在哪里
- 排序方式
- 降序排列
如您所见,它非常有限。忘掉连接和分组运算符,Any
、Contains
、Count
、First
、Last
等。唯一的好处是它很容易记住 :)
我怎么知道的?好吧,像往常一样,当文档中的内容不清楚时,要么使用试错法,要么使用反编译器。显然在这种情况下前者不适用,所以我使用了后者。如果你好奇,使用你最喜欢的反编译器并检查Microsoft.Azure.Documents.Client.dll
.
DocumentQueryEvaluator
的代码
你为什么不试试这个?
List<Art> items = DocumentDbHelper.Client.CreateDocument(collection.DocumentsLink)
.Where(i => i.type == "art" && i.Products.Any(p => p.Name == productType))
.AsEnumerable()
.ToList();
目前最高效的解决方案是使用 SQL 语法,因为它允许文档数据库使用集合的索引。
例子:
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