简体   繁体   English

C# LINQ .Any 不适用于 DocumentDb CreateDocumentQuery

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

I'm trying to query Art that has a product of a certain type.我正在尝试查询具有某种类型产品的 Art。 Here is my model for Art:这是我的艺术模型:

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

From here all I'm doing is the following LINQ query:从这里我所做的就是以下 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();

I get the following error:我收到以下错误:

"Method 'Any' is not supported."

I went to the page that the code references for seeing what is supported but I don't see it saying that Any() is not supported, so I'm probably doing something incorrect.我转到代码引用的页面以查看支持的内容,但我没有看到它说 Any() 不受支持,所以我可能做错了什么。 Any help is appreciated.任何帮助表示赞赏。

UPDATE更新

This is really odd to me, so I broke it up to see what was being returned from the two results to better debug the issue to this:这对我来说真的很奇怪,所以我分解了它以查看从两个结果中返回的内容,以便更好地将问题调试为:

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

For some reason this works, I'm not a fan of this because since I'm converting it to a list it's running the query twice - but it is at least proof that Any() and Select() should technically work.出于某种原因,这行得通,我不喜欢这个,因为因为我将它转换为一个列表,它运行了两次查询 - 但它至少证明 Any() 和 Select() 在技术上应该有效。

One of the biggest confusion with LINQ queries against IQueryable<T> is that they look exactly the same as queries against IEnumerable<T> .与针对IQueryable<T> LINQ 查询的最大混淆之一是它们看起来与针对IEnumerable<T>查询完全相同。 Well, the former is using Expression<Func<..>> whenever the later is using Func<..> , but except if one is using explicit declarations this is not so noticeable and seems unimportant.好吧,前者在后者使用Func<..>时使用Expression<Func<..>> ,但除非使用显式声明,否则这不是那么明显并且似乎不重要。 However, the big difference comes at runtime.然而,最大的区别出现在运行时。

Once the IEnumerable<T> query is successfully compiled, at runtime it just works, which is not the case with IQueryable<T> .一旦IEnumerable<T>查询被成功编译,在运行时它就可以工作,而IQueryable<T>则不是这种情况。 A IQueryable<T> query is actually an expression tree which is processed at runtime by the query provider. IQueryable<T>查询实际上是由查询提供程序在运行时处理的表达式树。

From one side this is a big benefit, from the other side, since the query provider is not involved at query compile time (all the methods are provided as extension methods by Queryable class), there is no way to know if the provider supports some construct/method or not until runtime.一方面这是一个很大的好处,另一方面,由于查询提供程序在查询编译时不涉及(所有方法都由Queryable类作为扩展方法提供),因此无法知道提供程序是否支持某些构造/方法或直到运行时。 People that use Linq to Entities know that very well.使用 Linq to Entities 的人非常了解这一点。 To make the things harder, there is no clear documentation what the specific query provider supports and more importantly, what it doesn't support (as you noticed from the "what is supported" link you provided).为了让事情变得更难,没有明确的文档说明特定查询提供程序支持什么,更重要的是,它不支持什么(正如您从提供的“支持什么”链接中注意到的那样)。

What's the solution?解决办法是什么? (and why your second code works) (以及为什么您的第二个代码有效)

The trick is to write the maximum possible (iesupported by the query provider) query part against the IQueryable<T> , and then switch to IEnumerable<T> and do the rest (remember, once compiled, IEnumerable<T> query just works).诀窍是针对IQueryable<T>编写最大可能的(即由查询提供程序支持的)查询部分,然后切换到IEnumerable<T>并执行其余操作(记住,一旦编译, IEnumerable<T>查询就可以工作) . The switch is performed by AsEnumerable() call.切换由AsEnumerable()调用执行。 And that's why your second code is working - because unsupported Any method is no more in the DocumentDb query provider context.这就是为什么您的第二个代码正在工作 - 因为不受支持的Any方法不再出现在 DocumentDb 查询提供程序上下文中。 Note that ToList call is not needed and the query is not executed twice - in fact this way there is no single query, but two - one in database and one in memory.请注意,不需要调用ToList并且查询不会执行两次 - 实际上这种方式没有单个查询,而是两个 - 一个在数据库中,一个在内存中。

So something like this would be sufficient:所以这样的事情就足够了:

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

Finally, what really is supported by the DocumentDb query provider最后,DocumentDb 查询提供程序真正支持的是什么

It's not quite clear from the documentation, but the answer is: exactly (and only) what is included there.从文档中不太清楚,但答案是:完全(且仅)包含在那里的内容。 In other words, the only supported query operators (or better say Queryable or Enumerable extension methods) are换句话说,唯一支持的查询操作符(或者更好的说法是QueryableEnumerable扩展方法)是

  • Select选择
  • SelectMany多选
  • Where在哪里
  • OrderBy订购方式
  • OrderByDescending按降序排列

As you may see, it's very limited.如您所见,它非常有限。 Forget about join and grouping operators, Any , Contains , Count , First , Last etc. The only good thing is that it's easy memorizable :)忘记连接和分组运算符, AnyContainsCountFirstLast等。唯一的好处是它很容易记住:)

How do I know that?我怎么知道? Well, as usual when something is unclear from the documentation, one either use trial and error or decompiler.好吧,像往常一样,当文档中的某些内容不清楚时,可以使用试错法或反编译器。 Apparently in this case the former is not applicable, so I've used the later.显然在这种情况下前者不适用,所以我使用了后者。 If you are curious, use your favorite decompiler and check the code of the internal class DocumentQueryEvaluator inside the Microsoft.Azure.Documents.Client.dll .如果您好奇,请使用您最喜欢的反编译器并检查Microsoft.Azure.Documents.Client.dll内部类DocumentQueryEvaluator的代码。

I am using the latest Azure DocumentDB nuget targetting .Net 4.6.我正在使用针对 .Net 4.6 的最新 Azure DocumentDB nuget。

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

Here's the sample code which is working fine for me.这是对我来说很好用的示例代码。

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

You should try using IEnumerable.Contains link here您应该尝试在此处使用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();

The most performant solution currently is to use the SQL syntax since that allows document DB to use the collection's index.当前最高效的解决方案是使用 SQL 语法,因为它允许文档 DB 使用集合的索引。
example:例子:

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

The disadvantage is that you might get non-unique results and have to distinct the result client-side.缺点是您可能会得到不唯一的结果,并且必须在客户端区分结果。

To get this issue into DocumentDB, it would help to vote on the following item: https://feedback.azure.com/forums/263030-documentdb/suggestions/14829654-support-sub-query-functions-like-exists-not-exist要将此问题纳入 DocumentDB,对以下项目进行投票会有所帮助: https : //feedback.azure.com/forums/263030-documentdb/suggestions/14829654-support-sub-query-functions-like-exists-not -存在

Why don't you try this one?你为什么不试试这个?

 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