The code block below answers the question: "How do you perform a left outer join using linq extension methods?"
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});
How do you write this GroupJoin and SelectMany as MethodCallExpressions? All of the examples that I've found are written using DynamicExpressions translating strings into lambdas (another example). I like to avoid taking a dependency on that library if possible.
Can the query above be written with Expressions and associated methods?
I know how to construct basic lambda expressions like foo => foo.Foo_Id
using ParameterExpressions MemberExpressions and Expression.Lambda() , but how do you construct (x,y) => new { Foo = x, Bars = y })
??? to be able to construct the necessary parameters to create both calls?
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)));
Ultimately, I need to create a repeatable process that will left join n Bars to Foo. Because we have a vertical data structure, a left-joined query is required to return what is represented as Bars, to allow the user to sort Foo. The requirement is to allow the user to sort by 10 Bars, but I don't expect them to ever use more than three. I tried writing a process that chained the code in the first block above up to 10 times, but once I got passed 5 Visual Studio 2012 start to slow and around 7 it locked up.
Therefore, I'm now trying to write a method that returns the selectManyCall and calls itself recursively as many times as is requested by the user.
Based upon the query below that works in LinqPad, the process that needs to be repeated only requires manually handling the transparent identifiers in Expression objects. The query sorts returns Foos sorted by Bars (3 Bars in this case).
A side note. This process is significantly easier doing the join in the OrderBy delegate, however, the query it produces includes the T-SQL "OUTER APPLY", which isn't supported by Oracle which is required.
I'm grateful for any ideas on how to write the projection to anonymous type or any other out-of-the-box idea that may work. Thank you.
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);