locked
How can I create reusable expression trees? RRS feed

  • Question

  • I have this expression tree:

        internal static IEnumerable<ElemType> WhereIn<ElemType>(this IEnumerable<ElemType> query, string filterFieldName, string[] values)
        {
            ParameterExpression lambdaParam = Expression.Parameter(typeof(ElemType), "p");
            MethodCallExpression paramToString = Expression.Call(Expression.PropertyOrField(lambdaParam, filterFieldName), typeof(object).GetMethod("ToString"));
            MethodInfo mi = typeof(Enumerable).GetMethods().Where(x => string.Equals(x.Name, "Contains", StringComparison.OrdinalIgnoreCase)).Single(x => x.GetParameters().Length == 2).MakeGenericMethod(typeof(string));
            Expression<Func<string[]>> array = () => values;
            MethodCallExpression contains = Expression.Call(mi, array.Body, paramToString);
            LambdaExpression lambdaExp = Expression.Lambda<Func<ElemType, bool>>(contains, lambdaParam);
            Func<ElemType, bool> lambda = (Func<ElemType, bool>)lambdaExp.Compile();
    
            return query.Where(lambda);
        }

    Now, when calling it using something like query.WhereIn("propName", new string[] {"aaa", "bbb"}) I don't always want the code to create a new expression tree as it's very time consuming to do this. But the .Compile() function doesn't come with parameters which would allow me to use a compiled lambda. (Or, rather, I just don't understand how to utilize it correctly.)

    How would the above expression tree needed to be rewritten to allow me to cache the compiled result and provide a string array to the resulting compiled lambda?


    Still people out there alive using the keyboard?

    Working with SQL Server/Office/Windows and their poor keyboard support they seem extinct...

    • Edited by BetterToday Thursday, October 22, 2015 10:08 AM
    Thursday, October 22, 2015 9:09 AM

Answers

  • >But I don't know what to put into the cache so I can just execute the prepared expression tree without being >required to create/compile it again.

    You've hard-coded the runtime arguments.

    So first instead of:

     Func<ElemType, bool> lambda = (Func<ElemType, bool>)lambdaExp.Compile();

    create a lambda

     Func<ElemType, string[] ,bool> lambda = ...

    so you can call it like this:

    return query.Where(lambda,values);

    That compiled Func<ElemType, string[] ,bool> could then be put in a cache keyed by (ElemType,filterFieldName). 

    David


    David http://blogs.msdn.com/b/dbrowne/

    • Proposed as answer by Fred Bao Wednesday, November 4, 2015 3:05 AM
    • Marked as answer by BetterToday Wednesday, November 4, 2015 11:07 AM
    Monday, October 26, 2015 12:02 PM

All replies

  • Hello BetterToday,

    What I understand from your description is you are trying to make a generic lambda expression, is it right? If so, unfortunately, it is not possible to create a lambda expression which has new generic parameters. In your case, what I suggest is you can re-use generic parameters on the containing methods or types but not create new ones.

    Regards.


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Friday, October 23, 2015 2:14 AM
  • Thank you for replying.

    Actually, to give a use case, what I'm trying to do is to create a data row filter for a JavaScript grid which provides me with columnName as a string, so I cannot use the standard Linq lambda functions here.

    In the end I want to write something like this:

    string[] filter = new string[]{ "Contoso", "Northwind" };
    
    dataRows.WhereIn(typeof(Customer), "CustomerName", filter);


    What I want is to create an Expression tree cache for each of the possible types, and perhaps even for each possible property from each of these types:

    public class TKey
    {
      public Type type;
      public string propertyName;
    }
    
    Dictionary<TKey, Expression> cache = new Dictionary<TKey, Expression>;

    Finally I want my WhereIn() code to search this cache for the corresponding LambdaExpression and run it using an arbitrary value (in my example a string array):

    IEnumerable<ElemType> WhereIn<ElemType>(this IEnumerable<ElemType> query, string propertyName, string[] values)
    {
      TKey key = new TKey() { typeof(ElemType), propertyName };
    
      if (!cache.ContainsKey(key))
      {
        // create new lambda expression here
      }
    
      return cache[key](values);
    }

    But I don't know what to put into the cache so I can just execute the prepared expression tree without being required to create/compile it again.


    Still people out there alive using the keyboard?

    Working with SQL Server/Office/Windows and their poor keyboard support they seem extinct...

    • Edited by BetterToday Friday, October 23, 2015 10:29 AM
    Friday, October 23, 2015 10:27 AM
  • >>what I'm trying to do is to create a data row filter for a JavaScript grid which provides me with columnName as a string, so I cannot use the standard Linq lambda functions here.

    Have you tried with the the LINQ Dynamic Query Library which could let you make a linq query with a string column name directly:

    http://weblogs.asp.net/scottgu/dynamic-linq-part-1-using-the-linq-dynamic-query-library

    Regards.


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Monday, October 26, 2015 2:00 AM
  • This sounds intriguing but it won't work for my wherein expression.

    My primary desire is to learn how to create precompiled expressions which accept parameters. So the Dynamic Query Library is nice (and I will definitively make use of it in future scenarios) but it's not the answer I'm looking for.


    Still people out there alive using the keyboard?

    Working with SQL Server/Office/Windows and their poor keyboard support they seem extinct...

    Monday, October 26, 2015 9:42 AM
  • >But I don't know what to put into the cache so I can just execute the prepared expression tree without being >required to create/compile it again.

    You've hard-coded the runtime arguments.

    So first instead of:

     Func<ElemType, bool> lambda = (Func<ElemType, bool>)lambdaExp.Compile();

    create a lambda

     Func<ElemType, string[] ,bool> lambda = ...

    so you can call it like this:

    return query.Where(lambda,values);

    That compiled Func<ElemType, string[] ,bool> could then be put in a cache keyed by (ElemType,filterFieldName). 

    David


    David http://blogs.msdn.com/b/dbrowne/

    • Proposed as answer by Fred Bao Wednesday, November 4, 2015 3:05 AM
    • Marked as answer by BetterToday Wednesday, November 4, 2015 11:07 AM
    Monday, October 26, 2015 12:02 PM