特定のタイプの製品があるアートを照会しようとしています。これが私のアートのモデルです。
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."
サポートされているもの を参照するためにコードが参照しているページに行きましたが、Any()がサポートされていないということはわかりません。どんな助けも大歓迎です。
[〜#〜] update [〜#〜]
これは本当に奇妙なことなので、2つの結果から何が返されているのかを確認するために、問題をよりよくデバッグするために解散しました。
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();
何らかの理由でこれは機能しますが、リストに変換しているのでクエリを2回実行しているため、私はこれのファンではありません-しかし、少なくともAny()とSelect()が技術的に機能するはずです。
_IQueryable<T>
_に対するLINQクエリの最大の混乱の1つは、_IEnumerable<T>
_に対するクエリとまったく同じに見えることです。前者は_Expression<Func<..>>
_を使用していますが、後者は_Func<..>
_を使用していますが、明示的な宣言を使用している場合を除き、これはそれほど目立たず、重要ではないようです。ただし、実行時に大きな違いが生じます。 _IEnumerable<T>
_クエリが正常にコンパイルされると、実行時に正常に機能しますが、これは_IQueryable<T>
_の場合とは異なります。 _IQuaryable<T>
_クエリは、実際にはクエリプロバイダーによって実行時に処理される式ツリーです。クエリプロバイダーはクエリのコンパイル時に関与しないため(一方のメソッドはQueryable
クラスによって拡張メソッドとして提供されます)、一方からはこれは他方からは大きな利点です。プロバイダーが実行時まで何らかの構成/メソッドをサポートしているかどうか。 Linq to Entitiesを使用している人々は、それをよく知っています。物事を難しくするために、特定のクエリプロバイダーがサポートするもの、さらに重要なことは、サポートしないもの(提供した「サポート対象」リンクからわかるように)には明確なドキュメントがありません。
解決策は何ですか(および2番目のコードが機能する理由)
トリックは、_IQueryable<T>
_に対して可能な限り(クエリプロバイダーでサポートされている)クエリパーツを記述し、_IEnumerable<T>
_に切り替えて残りを実行することです(コンパイルしたら、_IEnumerable<T>
_クエリは機能します)。切り替えは、AsEnumerable()
呼び出しによって実行されます。それが、2番目のコードが機能している理由です-サポートされていないAny
メソッドがDocumentDbクエリプロバイダーコンテキストにないためです。 ToList
呼び出しは不要であり、クエリは2回実行されないことに注意してください。実際、この方法では1つのクエリではなく、2つ-データベースに1つ、メモリに1つです。したがって、このようなもので十分です
_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
拡張メソッドを言う)は
ご覧のとおり、非常に限られています。結合演算子とグループ化演算子、Any
、Contains
、Count
、First
、Last
などは忘れてください。唯一の良い点は、覚えやすい:)
それをどうやって知るのですか?さて、いつものように、ドキュメントから何かが不明な場合は、試行錯誤または逆コンパイラを使用します。この場合、明らかに前者は適用されないので、後者を使用しました。興味がある場合は、お気に入りのデコンパイラを使用して、_Microsoft.Azure.Documents.Client.dll
_内の内部クラスDocumentQueryEvaluator
のコードを確認してください。
.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
リンクはこちら を使用してみてください。
DbHelper.Client.CreateDocumentQuery<Art>(collection.DocumentsLink)
.Where(i => i.type == "art")
.Where(i => i.Products
.Select(p => p.Name).Contains(productType))
.AsEnumerable()
.ToList();
現在、最もパフォーマンスの高いソリューションは、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
これを試してみませんか?
List<Art> items = DocumentDbHelper.Client.CreateDocument(collection.DocumentsLink)
.Where(i => i.type == "art" && i.Products.Any(p => p.Name == productType))
.AsEnumerable()
.ToList();