推定原因
一般に、XPO はほとんどの場合「フリー ジョイン」をサポートしていません。ナレッジベースまたは Q/A サイトのどこかに明示的に書かれています。その記事をもう一度ヒットしたら、リンクを含めます。
元のコード例では、INNER クエリで「フリー ジョイン」を実行しようとしていました。「WHERE」句はキーによる結合を行っていましたが、おそらくナビゲーションですが、おそらく関係の定義の一部ではない「修飾子」による追加のフィルターも含まれていました。
さらに、クエリIQueryable<PRICE> o
は内部クエリで を再利用しようとしましたが、これは実際には XPO でいくらかサポートされているようです。
XPO は、XPObjects で定義されたプロパティおよび/または xpcollections によって形成されたパスに沿って、ナビゲーション結合のみをサポートするとドキュメントに記載されています。これは XPO 全体に当てはまるので、XPQuery にも当てはまります。他のすべての種類の結合は「自由結合」と呼ばれ、次のいずれかです。
- 関連するオブジェクトをフェッチし、それらからキー値を抽出し、WHERE-id-IN-(@p0,@p1,@p2, ...) - しかし、これはいくつかの最も単純なケースでのみ発生します
- または「完全にはサポートされていません」。つまり、例外がスローされ、クエリを手動で分割するか、言い換える必要があります。
考えられる直接解決策
ITEM_ID がリレーションであり、PRICE クラスの XPCollection である場合、クエリを書き直して、PRICEオブジェクトを取得し、結果オブジェクトを構築して、そのフィールドを PRICEオブジェクトのプロパティで初期化することができます。何かのようなもの:
return new ItemPricesViewModel()
{
Source = (from o in XpoSession.Query<PRICE>().AsEnumerable()
select new ItemPriceViewModel()
{
ID = o.ITEM_ID.ITEM_ID,
ItemCod = o.ITEM_ID.ITEM_COD,
....
ItemModifierID = o.ITEM_MODIFIER_ID.ITEM_MODIFIER_ID,
ItemPrices = (from d in o
where d.ITEM_ID.ITEM_ID == ....
select new Price()
.... .... ....
};
クエリを中断し、クエリを変換しようとするだけでなく、PRICE オブジェクトが最初にフェッチされるようにする「AsEnumerable」に注意してください。これが「うまくいく」可能性は非常に高いです。
また、クエリを明示的なステージに分割すると、XPO がクエリを分析するのに役立つ場合があります。
return new ItemPricesViewModel()
{
Source = (from o in XpoSession.Query<PRICE>()
select new
{
id = o.ITEM_ID.ITEM_ID,
itemcod = o.ITEM_ID.ITEM_COD,
....
}
).AsEnumerable()
.Select(temp =>
select new ItemPriceViewModel()
{
ID = temp.id
ItemCod = temp.itemcod,
....
ItemPrices = (from d in XpoSession.Query<PRICE>()
where d.ITEM_ID.ITEM_ID == ....
select new Price()
.... .... ....
};
ここで、最初にサーバーから項目データをフェッチし、次に「クライアント」で項目を構築してから、必要なグループを構築することに注意してください。変数を参照できなくなったことに注意してくださいo
。これらの正確なケースと例では、当然のことながら、2 つ目 (分割) はおそらく最初のものよりもさらに遅くなるでしょう。次に、すでにフェッチされた PRICE に基づいてメモリ内のグループを計算します。これは私の怠惰の副作用ではありませんが、LINQ クエリを書き直すときによくある落とし穴なので、警告として含めました :)
これらのコード例は両方とも、特にテーブルに多くの PRICE がある場合、パフォーマンスが非常に低くなる可能性が高いため、あなたのケースには推奨されません。XPO が窒息することなくクエリを食べられるように、クエリを書き直してその構造を単純化する方法の例として、これらを含めました。ただし、パフォーマンスを非常に簡単に損なう可能性があるため、細心の注意を払う必要があります。
観察と実際の解
ただし、元のクエリよりもそれほど悪くないことに注意してください。「ITEM_ID」で te 行をグループ化し、結果を個別のオブジェクトとしてフォーマットするためだけに、テーブルから O(N^2) 行フェッチに近い何かを実行しようとしたため、それ自体は非常に貧弱でした。適切に行われると、O(N lg N)+O(N) のようなものになるため、サポートされているかどうかに関係なく、GroupBy を使用した別の試みは確かにはるかに優れたアプローチであり、自分で見つけてくれて本当にうれしいです。 .
上で行ったように XPQuery 式を分割/単純化しようとすると、暗黙のうちに問題を再考し、最初はサポートされていなかったか、単にクラッシュしていたクエリを表現するためのより簡単で単純な方法を見つけることがよくあります。
残念ながら、あなたのクエリは実際には非常に単純なものでした。「簡単に言い換える」ことができない非常に複雑なクエリの場合、ステージに分割し、結合フィルターの一部を「クライアント」で機能させることは避けられません..私たちはそれに耐えるか、単純な直接手作りのSQLを使用する必要があります..
サイドノート:
XPO 全体で「フリー ジョイン」に問題があり、XPQuery だけでなく、XPCollection、XPView、CriteriaOperators などでも「完全にはサポートされていません」。ただし、少なくとも「私のバージョン」の DX11 では、XPQuery の LINQ サポートがまったく不十分であることに注意してください。
適切な LINQ クエリが次のような多くのケースに遭遇しました。
- 主にFreeJoinsで「NotSupportedException」をスローしますが、複雑なGroupByまたはSelect-with-Projection、GroupJoin、および他の多くのものでも非常に頻繁にスローします-時にはDistinct(!)でさえ誤動作しているように見えました
- いくつかの適切な型変換で「NullReferenceExceptions」をスローします (XPO は、INT/NULL をオブジェクトとして保持する列を解釈しようとしました
foo!=null && foo.bar!=123
.. foo = 123
) public int Foo {get;set;}
。すべては、DX がデータベース内の NULL を適切に処理できなかったためです (XPO がこのプロパティの nullable-INT 列を作成したためです..しかし、それは別の話です)。
- 他の構造から他のランダムな ArgumentException/InvalidOperation 例外をスローする
または、クエリ構造を不適切に分析することさえあります。たとえば、これは通常有効です。
session.Query<ABC>()
.Where( abc => abc.foo == "somefilter" )
.Select( abc => new { first = abc, b = abc } )
.ToArray();
しかし、このようなものは通常スローされます:
session.Query<ABC>()
.Select( abc => new { first = abc, b = abc } )
.Where ( temp => temp.first.foo == "somefilter" )
.ToArray();
しかし、これは有効です:
session.Query<ABC>()
.Select( abc => new { first = abc, b = abc } )
.ToArray()
.Where ( temp => temp.first.foo == "somefilter" )
.ToArray();
中間のコード例は、通常、XPO レイヤーが ABC クラス内の「.first.foo」パスを見つけようとしていたことを示すエラーをスローします。これは、その時点で要素の型がABC
もはやではなく、代わりにa'
匿名クラスであるため、明らかに間違っています。
免責事項
すでに指摘しましたが、繰り返します。これらの観察結果は DX11 に関連しており、おそらくそれ以前にも関連しています。DX12以降で何が修正されたのかわかりません(もし何かあったとしても!)。