Search code examples
c#lambdaexpressionexpression-trees

How to create dynamic lambda join query using expression in C#?


I am new to dynamic expression query. I want to create a join query dynamically. Below it the query for which I want the equivalent dynamic query:

var lstNums = new List<int> { 100, 101 }; 
var getAll = new StudentRepository().GetAll();   // returns IQueryable<Student>

var query = getAll.Join(lstNums, a => a.StudentId, b => b, (a, b) => a).ToList(); 
  1. The lstNums can be list of any primitive data types
  2. The getAll contains IQueryable<Student>, this can be IQueryable<T> of any entity
  3. The query will contain the List<Student> records after doing join with the getAll and list of integers. But the result can be any list of entity. The list i.e. lstNum can be any list of primitive data types.

This is what I have tried:

public static IQueryable JoinQuery(this IQueryable outer, IEnumerable innerEntities, string firstEntityPropName, 
    Type typeSecondEntity, Type typeResultEntity, params object[] values)
{
    LambdaExpression outerSelectorLambda = DynamicLinq.DynamicExpression.ParseLambda(outer.ElementType, null, firstEntityPropName, values);
   
    ParameterExpression expnInput = Expression.Parameter(typeSecondEntity, "inner");
    ParameterExpression expnResult = Expression.Parameter(typeResultEntity, "outer");               

    return outer.Provider.CreateQuery(
       Expression.Call(
           typeof(Queryable), "Join",
           new Type[] { outer.ElementType, innerEntities.AsQueryable().ElementType, outerSelectorLambda.Body.Type, expnResult.Type },
           outer.Expression, innerEntities.AsQueryable().Expression, Expression.Quote(outerSelectorLambda), expnInput,
           expnResult));
}

The extension method:

public static IQueryable<T> JoinQuery<T>(this IQueryable outer, IEnumerable innerEntities, string firstEntityPropName, Type typeSecondEntity, Type typeResultEntity, params object[] values)
{
    return (IQueryable<T>)Extensions.JoinQuery((IQueryable)outer, (IEnumerable)innerEntities, firstEntityPropName, typeSecondEntity, typeResultEntity, values);
}

Note: I have installed System.Linq.Dynamic nuget package version 1.0.7 for dynamic linq expression.


Solution

  • The below code worked for me:

    using DynamicLinq = System.Linq.Dynamic;
    using LinqExpression = System.Linq.Expressions;
    
    public static IQueryable JoinQuery(this IQueryable outer, IEnumerable innerEntities, string firstEntityPropName, 
        Type typeSecondEntity, Type typeResultEntity, params object[] values)
    {
        if (innerEntities == null) throw new ArgumentNullException(nameof(innerEntities));
        if (firstEntityPropName == null) throw new ArgumentNullException(nameof(firstEntityPropName));
        if (typeSecondEntity == null) throw new ArgumentNullException(nameof(typeSecondEntity));
        if (typeResultEntity == null) throw new ArgumentNullException(nameof(typeResultEntity));
    
        LambdaExpression outerSelectorLambda = DynamicLinq.DynamicExpression.ParseLambda(outer.ElementType, null, firstEntityPropName, values);
    
        ParameterExpression expnInput = Expression.Parameter(typeSecondEntity, "inner");
    
        ParameterExpression[] parameters = new ParameterExpression[] {
        Expression.Parameter(outer.ElementType, "outer"), Expression.Parameter(innerEntities.AsQueryable().ElementType, "inner")
        };
    
        LambdaExpression selectorSecondEntity = DynamicLinq.DynamicExpression.ParseLambda(new ParameterExpression[] { expnInput }, typeSecondEntity, "inner");
        LambdaExpression selectorResult = DynamicLinq.DynamicExpression.ParseLambda(parameters, typeResultEntity, "outer");
    
        return outer.Provider.CreateQuery(
           Expression.Call(
               typeof(Queryable), "Join",
               new Type[] { outer.ElementType, innerEntities.AsQueryable().ElementType, outerSelectorLambda.Body.Type, selectorResult.Body.Type },
               outer.Expression, innerEntities.AsQueryable().Expression, Expression.Quote(outerSelectorLambda), Expression.Quote(selectorSecondEntity),
              Expression.Quote(selectorResult)));
    }
    

    The extension method is as follows:

    public static IQueryable<T> JoinQuery<T>(this IQueryable outer, IEnumerable innerEntities, string firstEntityPropName, Type typeSecondEntity, Type typeResultEntity, params object[] values)
    {
        return (IQueryable<T>)Extensions.JoinQuery((IQueryable)outer, (IEnumerable)innerEntities, firstEntityPropName, typeSecondEntity, typeResultEntity, values);
    }