I am trying to mimick the left outer join here but using dynamic linq extension methods. What i have:
public static IQueryable SelectMany(this IQueryable source, string selector, string resultsSelector, params object[] values) { if (source == null) throw new ArgumentNullException("source"); if (selector == null) throw new ArgumentNullException("selector"); // Parse the lambda LambdaExpression lambda = DynamicExpression.ParseLambda( source.ElementType, null, selector, values); // Fix lambda by recreating to be of correct Func<> type in case // the expression parsed to something other than IEnumerable<T>. // For instance, a expression evaluating to List<T> would result // in a lambda of type Func<T, List<T>> when we need one of type // an Func<T, IEnumerable<T> in order to call SelectMany(). Type inputType = source.Expression.Type.GetGenericArguments()[0]; Type resultType = lambda.Body.Type.GetGenericArguments()[0]; Type enumerableType = typeof(IEnumerable<>).MakeGenericType(resultType); Type delegateType = typeof(Func<,>).MakeGenericType(inputType, enumerableType); lambda = Expression.Lambda(delegateType, lambda.Body, lambda.Parameters); ParameterExpression[] parameters = new ParameterExpression[] { Expression.Parameter(source.ElementType, "outer"), Expression.Parameter(resultType, "inner") }; LambdaExpression resultsSelectorLambda = DynamicExpression.ParseLambda( parameters, null, resultsSelector, values); // Create the new query return source.Provider.CreateQuery(Expression.Call(typeof(Queryable), "SelectMany", new Type[] { source.ElementType, resultType, resultsSelectorLambda.Body.Type }, source.Expression, Expression.Quote(lambda), Expression.Quote(resultsSelectorLambda))); }
and:
public static IQueryable GroupJoin(this IQueryable outer, IEnumerable inner, string outerKeySelector, string innerKeySelector, string resultSelector, params object[] values) { Type innerElementType = inner.AsQueryable().ElementType; var outerParameter = Expression.Parameter(outer.ElementType, "outer"); var innerParameter = Expression.Parameter(innerElementType, "inner"); var groupParameter = Expression.Parameter(typeof(IEnumerable<>) .MakeGenericType(innerElementType), "group"); var outerLambda = DynamicExpression.ParseLambda(new[] { outerParameter }, null, outerKeySelector, values); var innerLambda = DynamicExpression.ParseLambda(new[] { innerParameter }, outerLambda.Body.Type, innerKeySelector, values); var resultLambda = DynamicExpression.ParseLambda(new[] { outerParameter, groupParameter }, null, resultSelector, values); return outer.Provider.CreateQuery(Expression.Call(typeof(Queryable), "GroupJoin", new[] { outer.ElementType, innerElementType, outerLambda.Body.Type, resultLambda.Body.Type }, outer.Expression, Expression.Constant(inner), Expression.Quote(outerLambda), Expression.Quote(innerLambda), Expression.Quote(resultLambda))); }
However where I fall down is with the DefaultIfEmpty
within the SelectMany
A left outer join is a join in which each element of the first collection is returned, regardless of whether it has any correlated elements in the second collection. You can use LINQ to perform a left outer join by calling the DefaultIfEmpty method on the results of a group join.
One commonly used feature of Language-Integrated Query (LINQ) is the facility to combine two sequences of related data using joins. The standard join operation provides an inner join but with a minor modification can be changed to give a left outer join.
The Dynamic LINQ library exposes a set of extension methods on IQueryable corresponding to the standard LINQ methods at Queryable, and which accept strings in a special syntax instead of expression trees.
Add void DefaultIfEmpty();
to interface IEnumerableSignatures
Then use
public static object DefaultIfEmpty(this IQueryable source) { if (source == null) throw new ArgumentNullException("source"); return source.Provider.Execute( Expression.Call( typeof(Queryable), "DefaultIfEmpty", new Type[] { source.ElementType }, source.Expression)); }
Then you have a call like
var qry = Foo.GroupJoin(Bar, "outer.Id", "inner.Id", "new(outer.Id as Foo, group as Bars)").SelectMany("Bars.DefaultIfEmpty()", "new(outer.Foo as Foo, inner as Bar)");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With