Search code examples
c#linqexpressionexpression-trees

Can't convert this linq to dynamic expression tree


I'm trying to create an expression that uses FirstOrDefault with predicate on an expression of IEnumerable<TComparable> but it is giving me this error: The parameter 'o' was not bound in the specified LINQ to Entities query expression

I have a linq expression like this:

IEnumerable<string> names = new List<string>() { "abc", "def", "ghi" };
string name = names.FirstOrDefault(o => o.Contains("abc"));

public static Expression FirstOrDefault(this Expression expression, Type collectionValuesType, MethodInfo comparerMethod, string keyword)
{
    MethodInfo firstOrDefaultMethod = typeof(Enumerable).GetMethods()
        .FirstOrDefault(o => o.Name == "FirstOrDefault" && o.GetParameters().Length == 2)
        .MakeGenericMethod(new Type[] { collectionValuesType });


  Type firstOrDefaultDelegateType = typeof(Func<,>).MakeGenericType(collectionValuesType, typeof(bool));
    ParameterExpression firstOrDefaultPredicateParameter = Expression.Parameter(collectionValuesType);


//THIS LINE binds the "o" in (o => o.Contains("abc")) , and it is where I'm stuck with since yesterday!
    MemberExpression firstOrDefaultParameterO = Expression.Property(expression, typeof(string).GetProperty(???)); 

//o => o.ComparerMethod(keyword)
MethodCallExpression firstOrDefaultCompareExpression = Expression.Call(
    firstOrDefaultParameterO,
    comparerMethod,
    Expression.Constant(keyword, typeof(string))
);

//expression.FirstOrDefault(firstOrDefaultCompareExpression);
return Expression.Call(
    firstOrDefaultMethod,
    expression,
    Expression.Lambda(firstOrDefaultDelegateType, firstOrDefaultCompareExpression, Expression.Parameter(collectionValuesType))
);
}

If I had a complex type I would be using like this:

public class Example { public string Name; }

//o => o.Name.Contains("abc"))
        MemberExpression firstOrDefaultParameterO = Expression.Property(expression, typeof(Example).GetProperty("Name")); 

The problem is that I don't know how to bind string type, since it does not have a Property that will give the property value.

BTW: collectionValuesType = typeof(string)

I have edited the question as suggested to make things clear.


Solution

  • You don't need to construct the Expression.Property for simple types like string.

    For example. If I have to build expression tree for OrderBy method for type Person with Name property, I'll build the expression tree like this:

    ParameterExpression pe = System.Linq.Expressions.Expression.Parameter(typeof(T), "p");
    Expression<Func<T, TPropertyType>> expr = System.Linq.Expressions.Expression.Lambda<Func<T, TPropertyType>>(System.Linq.Expressions.Expression.Property(pe, propertyName), pe);
    

    But for string type I'll simply do : (Since your expression will simply be x=>x for string types)

    If( typeof(T)==typeof(string))
    {
        ParameterExpression pe = System.Linq.Expressions.Expression.Parameter(typeof(T), "p");
        Expression<Func<T, TPropertyType>> expr = System.Linq.Expressions.Expression.Lambda<Func<T, TPropertyType>>(pe,pe);
    }
    

    You can probably use the same concept to fix your problem.