以下のコード ブロックは、「 linq 拡張メソッドを使用して左外部結合を実行するにはどうすればよいですか?」という質問に答えます。
var qry = Foo.GroupJoin(
Bar,
foo => foo.Foo_Id,
bar => bar.Foo_Id,
(x,y) => new { Foo = x, Bars = y })
.SelectMany(
x => x.Bars.DefaultIfEmpty(),
(x,y) => new { Foo = x, Bar = y});
この GroupJoin と SelectMany を MethodCallExpressions としてどのように記述しますか? 私が見つけたすべての例は、文字列をラムダに変換する DynamicExpressions を使用して記述されています (別の例)。可能であれば、そのライブラリに依存することは避けたいと思っています。
上記のクエリは、式と関連するメソッドで記述できますか?
foo => foo.Foo_Id
ParameterExpressions MemberExpressions と Expression.Lambda() を使用するような基本的なラムダ式を構築する方法は知っていますが、どのように構築します(x,y) => new { Foo = x, Bars = y })
か??? 両方の呼び出しを作成するために必要なパラメーターを構築できるようにするには?
MethodCallExpression groupJoinCall =
Expression.Call(
typeof(Queryable),
"GroupJoin",
new Type[] {
typeof(Customers),
typeof(Purchases),
outerSelectorLambda.Body.Type,
resultsSelectorLambda.Body.Type
},
c.Expression,
p.Expression,
Expression.Quote(outerSelectorLambda),
Expression.Quote(innerSelectorLambda),
Expression.Quote(resultsSelectorLambda)
);
MethodCallExpression selectManyCall =
Expression.Call(typeof(Queryable),
"SelectMany", new Type[] {
groupJoinCall.ElementType,
resultType,
resultsSelectorLambda.Body.Type
}, groupJoinCall.Expression, Expression.Quote(lambda),
Expression.Quote(resultsSelectorLambda)));
最終的に、n Bars を Foo に結合する繰り返し可能なプロセスを作成する必要があります。垂直方向のデータ構造があるため、ユーザーが Foo をソートできるように、バーとして表されるものを返すには、左結合クエリが必要です。要件は、ユーザーが 10 本のバーで並べ替えできるようにすることですが、3 本を超えるバーを使用することはないと思います。上記の最初のブロックのコードを最大 10 回チェーンするプロセスを作成しようとしましたが、5 回を超えると Visual Studio 2012 が遅くなり、7 回前後でロックアップしました。
したがって、selectManyCall を返し、ユーザーが要求した回数だけ再帰的に呼び出すメソッドを作成しようとしています。
LinqPad で機能する以下のクエリに基づいて、繰り返す必要があるプロセスは、Expression オブジェクトの透過的な識別子を手動で処理することだけです。クエリ sorts は、Bars (この場合は 3 Bars) でソートされた Foos を返します。
補足です。このプロセスは、OrderBy デリゲートで結合を行う方がはるかに簡単ですが、生成されるクエリには T-SQL "OUTER APPLY" が含まれています。これは、必要な Oracle でサポートされていません。
プロジェクションを匿名型に書き込む方法や、機能する可能性のあるその他の独創的なアイデアに感謝します。ありがとうございました。
var q = Foos
.GroupJoin (
Bars,
g => g.FooID,
sv => sv.FooID,
(g, v) =>
new
{
g = g,
v = v
}
)
.SelectMany (
s => s.v.DefaultIfEmpty (),
(s, v) =>
new
{
s = s,
v = v
}
)
.GroupJoin (
Bars,
g => g.s.g.FooID,
sv => sv.FooID,
(g, v) =>
new
{
g = g,
v = v
}
)
.SelectMany (
s => s.v.DefaultIfEmpty (),
(s, v) =>
new
{
s = s,
v = v
}
)
.GroupJoin (
Bars,
g => g.s.g.s.g.FooID,
sv => sv.FooID,
(g, v) =>
new
{
g = g,
v = v
}
)
.SelectMany (
s => s.v.DefaultIfEmpty (),
(s, v) =>
new
{
s = s,
v = v
}
)
.OrderBy (a => a.s.g.s.g.v.Text)
.ThenBy (a => a.s.g.v.Text)
.ThenByDescending (a => a.v.Date)
.Select (a => a.s.g.s.g.s.g);