式ツリーを手動で組み合わせて、標準のlinq演算子を使用しているように見えるレベルのモジュール性を実現しようとしています。このコードは基本的に、1つの式を使用して、他の2つの式のどちらを呼び出すかを決定する式ツリーを作成します。他の式の1つには、別の式を使用してそれ自体が取得される追加のパラメーターが必要です。このパラメーターは複数の値を取得するために使用されますが、アクセスごとにパラメーターを取得する式が繰り返されます。説明をわかりやすくするために、コードと出力を含めました。
public void Test() {
var parameters = ProjectionOne.Parameters;
Expression<Func<Foo, bool>> isType = f => f.TypeId == 1;
Expression<Func<Foo, Satellite>> satSelector = f => f.Satellites.Single();
var satelliteSelector = Expression.Invoke(satSelector, parameters[0]);
var test = Expression.Lambda<Func<Foo, Bar>>(
Expression.Condition(
Expression.Invoke(isType, parameters[0]),
Expression.Invoke(ProjectionOne, parameters[0]),
Expression.Invoke(ProjectionTwo, parameters[0], satelliteSelector)), parameters);
}
public Expression<Func<Foo, Bar>> ProjectionOne {
get {
return foo => new Bar() {
Id = foo.Id
};
}
}
public Expression<Func<Foo, Satellite, Bar>> ProjectionTwo {
get {
return (foo, sat) => new Bar() {
Id = foo.Id,
Start = sat.Start,
End = sat.End
};
}
}
データベースでこのクエリを実行すると、SQLは次のように生成されます。
SELECT [t0].[value], [t0].[value2] AS [Start], [t0].[value3] AS [End], [t0].[Id] AS [Id]
FROM (
SELECT
(CASE
WHEN [t0].[TypeId] = @p0 THEN 1
WHEN NOT ([t0].[TypeId] = @p0) THEN 0
ELSE NULL
END) AS [value], (
SELECT [t2].[Start]
FROM [dbo].[Satellite] AS [t2]
WHERE [t2].[Id] = [t0].[Id]
) AS [value2], (
SELECT [t2].[End]
FROM [dbo].[Satellite] AS [t2]
WHERE [t2].[Id] = [t0].[Id]
) AS [value3], [t0].[Id]
FROM [dbo].[Foo] ) AS [t0]
問題は、サブ選択が重複していることです。1つは「開始」値を取得し、もう1つは「終了」値を取得します。それらが両方とも単一のサブ選択から取得された方がはるかに良いでしょう。これを強制するために式ツリーの構造を変更するにはどうすればよいですか?また、このクエリを実行する簡単な方法があることも理解していますが、これはここに示されていないより大きなフレームワークの一部であり、再利用可能な式の大規模なセットから式ツリーを手動で組み立てることによってのみ実現できます。