Invoking Regex.IsMatch() inside a dynamic linq query

c# dynamic-linq regex

Question

I'm trying to invoke the Regex.IsMatch() and evaluate the returned result inside a dynamic linq query. This is what I tried:

public static LambdaExpression Parse(SearchQuery query)
{
    string compilableExpression = "Regex.IsMatch(Category.ToLower(), \"\\bSomeCat\\b\", RegexOptions.Compiled) == true";

    ParameterExpression parameter1 = System.Linq.Expressions.Expression.Parameter(typeof(EventListItem));
    ParameterExpression parameter2 = System.Linq.Expressions.Expression.Parameter(typeof(Regex));

    return System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { parameter1, parameter2 }, null, compilableExpression);
}

In this case Category is a property in the EventListItem. This exception is thrown upon calling ParseLambda():

Unknown identifier 'Regex'.

Is there a way to invoke the method? I came across Expression.Call() method, but I'm not sure if that's what I'm looking for. Any help is appreciated.

1
0
5/21/2013 9:24:27 PM

Accepted Answer

I haven't used System.Linq.Dynamic a lot, but here is a way to make your example work:

1 - You really only have one input object, your EventListItem, so remove parameter2 (Regex):

string compilableExpression = "Regex.IsMatch(Category.ToLower(), \"\\bSomeCat\\b\", RegexOptions.Compiled) == true";
ParameterExpression parameter1 = System.Linq.Expressions.Expression.Parameter(typeof(EventListItem));
return System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { parameter1 }, null, compilableExpression);

2 - DynamicExpression.ParseLambda() is made to read properties and methods from the input object. Using methods on other classes (here: Regex.IsMatch() is limited to a small set of predefined classes, and by default Regex isn't one of them.

Therefore, we somehow need to make the parser realize that "Regex" is a class and not a property on EventListItem. Assuming you have included the DynamicLinq.cs file in your project, this can be done by adding Regex (and RegexOptions) to the internal ExpressionParser.predefinedTypes array:

static readonly Type[] predefinedTypes = {
    typeof(Object),
    typeof(Boolean),
    ...

    typeof(System.Text.RegularExpressions.Regex), 
    typeof(System.Text.RegularExpressions.RegexOptions),
};


EDIT: Complex parameter values
If we need to include more complex parameters in our method calls, e.g. a combined RegexOptions enum; RegexOptions.Compiled | RegexOptions.IgnoreCase, ParseLambda also accepts a list of values.

We prepare the combined enum beforehand, and submit it in that values list. In compilableExpression we include placeholders for the values we submit, indexed in the same order we submit them (here we only have one value - index 0)

var options = RegexOptions.Compiled | RegexOptions.IgnoreCase;
string compilableExpression = "Regex.IsMatch(Category.ToLower(), \"\\bSomeCat\\b\", @0) == true";
ParameterExpression parameter1 = SLE.Expression.Parameter(typeof(EventListItem));
return SLD.DynamicExpression.ParseLambda(new[] { parameter1 },
                                         null,
                                         compilableExpression,
                                         options);

Bonus: Because the RegexOptions class/enum is no longer directly referenced in compilableExpression, we also no longer need to include RegexOptions in ExpressionParser.predefinedTypes.

7
5/23/2017 12:23:50 PM


Related Questions





Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow