views:

7476

answers:

7

There was a library of dynamic Linq extensions methods released as a sample with VS2008. I'd like to extend it with a Join method. The code below fails with a parameter miss match exception at run time. Can anyone find 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 now fixed it myself, here's the answer. Please vote it up or add a better one.

+5  A: 

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);
    }
Royd Brayshay
A: 

Cool stuff, Royd.

Question Please can you post an example where you select more than one column in parameter "resultsSelector"?

I tried it that way: var t1 = datacontext.GetTable(TableType1); var r = datacontext.GetTable(TableType2).Join(t1, "F86", "F91", "inner.F1, inner.F86");

That throws an "ParseException: Syntax Error". in System.Linq.Dynamic.ExpressionParser.ValidateToken()

Still trying. Gordian


OK. I found the answer. You have to use the new "key-word" to create a dynamicTye in resultset.

var r = datacontext.GetTable(TableType2).Join(t1, "F86", "F91", "new(inner.F1, inner.F86)");

Greetings Gordian

A: 

Can you post an example of using your join. I would like to use it, but I am such a greenhorn at LINQ. Thanks.

joe
A: 

Great job.

I came up with a very similar Join method. Have you tried implementing GroupJoin for the DynamicQueryable class? I have trouble defining the Func<TOuter, IQueryable<TInner>, TResult>> resultSelector lambda parameter.

A: 

Any results, does anybody have implemented GroupJoin for the DynamicQueryable class?

A: 

Thanks for this, great stuff. after much mucking around with the selector i found while i was debugging it you can use the "inner" and "outer" keywords

Soulburner
A: 

as great as this code seems to be its next to useless without a decent realworld example of how to actually perform the join and return some data from both tables, Ive spent ages trying to figure it out, can anyone supply some examples ?

mark