Extension method to do a left outer join with linq expressions
For usage see Queryable.Join reference:
http://msdn.microsoft.com/en-us/library/bb534644(v=vs.95).aspx.
It's uses a help class (JoinTuple<,>) for the groupjoin.
public static IQueryable<TResult> LeftOuterJoin<TOuter, TInner, TKey, TResult>(
this IQueryable<TOuter> outer,
IQueryable<TInner> inner,
Expression<Func<TOuter, TKey>> outerKeySelector,
Expression<Func<TInner, TKey>> innerKeySelector,
Expression<Func<TOuter, TInner, TResult>> resultSelector)
{
System.Type t1 = typeof(TOuter);
System.Type t2 = typeof(IEnumerable<TInner>);
System.Type groupJoinType = typeof(JoinTuple<TOuter, IEnumerable<TInner>>);
var param1 = Expression.Parameter(t1, "w");
var param2 = Expression.Parameter(t2, "f");
var groupJoinMemberInitLambda = Expression.Lambda(
Expression.MemberInit(
Expression.New(groupJoinType),
Expression.Bind(groupJoinType.GetProperty("Item1"), param1),
Expression.Bind(groupJoinType.GetProperty("Item2"), param2)
), param1, param2);
var q = outer.Provider.CreateQuery(
Expression.Call(
typeof(Queryable),
"GroupJoin",
new System.Type[]
{
typeof(TInner),
typeof(TOuter),
typeof(TKey),
groupJoinType
},
outer.Expression,
inner.Expression,
outerKeySelector,
innerKeySelector,
Expression.Quote(groupJoinMemberInitLambda)));
ParameterExpression defaultIfParameter = Expression.Parameter(groupJoinType, "f");
MethodCallExpression defaultIfEmptyExpression2 =
Expression.Call(
typeof(Enumerable),
"DefaultIfEmpty",
new System.Type[] { typeof(TInner) },
Expression.Property(defaultIfParameter, "Item2"));
LambdaExpression defaultIfEmptyLambda2 = Expression.Lambda(defaultIfEmptyExpression2, defaultIfParameter);
var p1 = Expression.Parameter(groupJoinType, "t");
var p2 = Expression.Parameter(typeof(TOuter), "o");
var resultInvokeExpression = Expression.Invoke(resultSelector, Expression.Property(p1, "Item1"), p2);
return q.Provider.CreateQuery(
Expression.Call(
typeof(Queryable),
"SelectMany",
new System.Type[]
{
groupJoinType,
typeof(TInner),
typeof(TResult)
},
q.Expression,
defaultIfEmptyLambda2,
Expression.Lambda(
resultInvokeExpression,
p1,
p2))) as IQueryable<TResult>;
}
public class JoinTuple<T1, T2>
{
public T1 Item1 { get; set; }
public T2 Item2 { get; set; }
}
Fork
0 Feedback
You must log in before you can give any feedback
You must log in before you can post a comment


554
0




Mark 'linq' tag as 'like'
Mark 'linq' tag as 'ignore'