There was a library of dynamic LINQ extensions methods released as a sample with Visual Studio 2008. I'd like to extend it with a join method. The code below fails with a parameter miss match exception at run time. Where is the problem?
public static IQueryable Join(this IQueryable outer, IEnumerable inner,
string outerSelector, string innerSelector, string resultsSelector,
params object[] values)
{
if (inner == null)
throw new ArgumentNullException("inner");
if (outerSelector == null)
throw new ArgumentNullException("outerSelector");
if (innerSelector == null)
throw new ArgumentNullException("innerSelector");
if (resultsSelector == null)
throw new ArgumentNullException("resultsSelctor");
LambdaExpression outerSelectorLambda =
DynamicExpression.ParseLambda(outer.ElementType, null,
outerSelector, values);
LambdaExpression innerSelectorLambda =
DynamicExpression.ParseLambda(inner.AsQueryable().ElementType,
null, innerSelector, values);
ParameterExpression[] parameters = new ParameterExpression[] {
Expression.Parameter(outer.ElementType, "outer"),
Expression.Parameter(inner.AsQueryable().ElementType,
"inner")
};
LambdaExpression resultsSelectorLambda =
DynamicExpression.ParseLambda(parameters, null,
resultsSelector, values);
return outer.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "Join", new Type[] {
outer.ElementType,
inner.AsQueryable().ElementType,
outerSelectorLambda.Body.Type,
innerSelectorLambda.Body.Type,
resultsSelectorLambda.Body.Type
},
outer.Expression, inner.AsQueryable().Expression,
Expression.Quote(outerSelectorLambda),
Expression.Quote(innerSelectorLambda),
Expression.Quote(resultsSelectorLambda))
);
}
I've fixed it myself now. It was a schoolboy error passing too many parameters to the CreateQuery(... ) call. Paste the following code into the Dynamic.cs file within the DynamicQueryable class for a dynamic Join extension method. You can find the source for the DynamicQuery sample project at http://code.msdn.microsoft.com/csharpsamples.
Enjoy.
public static IQueryable Join(this IQueryable outer, IEnumerable inner, string outerSelector, string innerSelector, string resultsSelector, params object[] values)
{
if (inner == null) throw new ArgumentNullException("inner");
if (outerSelector == null) throw new ArgumentNullException("outerSelector");
if (innerSelector == null) throw new ArgumentNullException("innerSelector");
if (resultsSelector == null) throw new ArgumentNullException("resultsSelctor");
LambdaExpression outerSelectorLambda = DynamicExpression.ParseLambda(outer.ElementType, null, outerSelector, values);
LambdaExpression innerSelectorLambda = DynamicExpression.ParseLambda(inner.AsQueryable().ElementType, null, innerSelector, values);
ParameterExpression[] parameters = new ParameterExpression[] {
Expression.Parameter(outer.ElementType, "outer"), Expression.Parameter(inner.AsQueryable().ElementType, "inner") };
LambdaExpression resultsSelectorLambda = DynamicExpression.ParseLambda(parameters, null, resultsSelector, values);
return outer.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "Join",
new Type[] {outer.ElementType, inner.AsQueryable().ElementType, outerSelectorLambda.Body.Type, resultsSelectorLambda.Body.Type },
outer.Expression, inner.AsQueryable().Expression, Expression.Quote(outerSelectorLambda), Expression.Quote(innerSelectorLambda), Expression.Quote(resultsSelectorLambda)));
}
//The generic overload.
public static IQueryable<T> Join<T>(this IQueryable<T> outer, IEnumerable<T> inner, string outerSelector, string innerSelector, string resultsSelector, params object[] values)
{
return (IQueryable<T>)Join((IQueryable)outer, (IEnumerable)inner, outerSelector, innerSelector, resultsSelector, values);
}