Searching by regexp when constructing LINQ expression using Expression classes - c#

Here is the post which describes the way to create an Expression<Func<MyClass, bool>> predicate dynamically. Here is a snippet:
var param = Expression.Parameter(typeof(string), "p");
var len = Expression.PropertyOrField(param, "SomeText");
var body = Expression.Equal(
len, Expression.Constant("Text"));
var lambda = Expression.Lambda<Func<string, bool>>(
body, param);
I wonder how do I apply a regexp to a string instead of Equality. Is there a possibility?
A possible pseudo code would be like:
var param = Expression.Parameter(typeof(string), "p");
var len = Expression.PropertyOrField(param, "SomeText");
var body = Expression.Regexp(
len, #"\D+");
var lambda = Expression.Lambda<Func<string, bool>>(
body, param);

You can use Expression.Call(Type type, string methodName, Type[] typeArguments, params Expression[] arguments) to call your test method that checks for regular expression.
List<string> lista = new List<string>() { "aaaa", "aaabb", "aaacccc", "eee" };
var param = Expression.Parameter(typeof(string), "s");
var pattern = Expression.Constant("\\Aa");
var test = Expression.Call(typeof(Regex), "IsMatch", null, param, pattern);
var lambda = Expression.Lambda<Func<string, bool>>(test, param);
IEnumerable<string> query = lista.Where(lambda.Compile());
foreach (string s in query)
{
Console.WriteLine(s);
}

Related

Expression to filter data based on a property which is a list of strings

I have the following code:
case FilterQueryType.Contains:
var parameterExp = Expression.Parameter(type, "type");
var propertyExp = Expression.Property(parameterExp, filter.PropertyName);
var containsConstExp = Expression.Constant(filter.MyKeyword);
MethodInfo method = typeof(string).GetMethod("Contains", new []{typeof(string)});
var containsMethodExp = Expression.Call(propertyExp, method, containsConstExp);
var containsLambda = Expression.Lambda<Func<T, bool>>(containsMethodExp, parameterExp);
items = items.Where(containsLambda);
break;
This code works fine as long as filter.PropertyName is a string.
Now I have a case where filter.PropertyName is actually an enumerable of strings.
Could someone tell me how can I create the correct expression for this? (filter.MyKeyword itself will always be a single value)
MemberExpression memberExpression = Expression.Property(parameterExp, filter.PropertyName);
Expression convertExpression = Expression.Convert(memberExpression, typeof(List<string>));
MethodCallExpression containsExpression = Expression.Call(convertExpression, "Contains", new Type[] { }, constExpr);
lambda = Expression.Lambda<Func<T, bool>>(containsExpression, parameterExp);
items = items.Where(lambda);
This solution works for me

IQueryable expression type mismatch

I want to create an extension method for a LINQ expression but I'm stuck. What I need is just to create a method which will add a specific Where clause to a Queryable. Something like:
var hierarchy = "a string";
Session.Query<SomeClass>.Where(x => x.Layer.Hierarchy.StartsWith(hierarchy) ||
x.Layer.Hierarchy == hierarchy);
to become:
var hierarchy = "a string";
Session.Query<SomeClass>.LayerHierarchy(x => x.Layer, hierarchy);
And do that Where logic inside. So basicly the extension method LayerHierarchy() is running over the Queryable of T but the subject is of type Layer:
public static IQueryable<T> LayerHierarchy<T>(this IQueryable<T> query,
Expression<Func<T, Layer>> layer,
string hierarchy)
{
var parameterExp = Expression.Parameter(typeof(Layer), "layer");
var propertyExp = Expression.Property(parameterExp, "Hierarchy");
// StartWith method
MethodInfo methodStartsWith = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
var valueStartsWith = Expression.Constant(string.Concat(hierarchy, "|"), typeof(string));
var methodExpStartsWith = Expression.Call(propertyExp, methodStartsWith, valueStartsWith);
var startsWith = Expression.Lambda<Func<Layer, bool>>(methodExpStartsWith, parameterExp);
// Equals method
MethodInfo methodEquals = typeof(string).GetMethod("Equals", new[] { typeof(string) });
var valueEquals = Expression.Constant(hierarchy, typeof(string));
var methodExpEquals = Expression.Call(propertyExp, methodEquals, valueEquals);
var equals = Expression.Lambda<Func<Layer, bool>>(methodExpEquals, parameterExp);
return query
.Where(startsWith)
.Where(equals);
}
Everything works fine above the return line. It complains that...
Cannot convert from System.Linq.Expressions.Expression<System.Func<Layer, bool>> to System.Linq.Expressions.Expression<System.Func<T, int, bool>>
when trying to pass the expressions to query.Where() method. How can I fix it?
Well, the problem is how you are creating the Lambdas. They should begin from T, not from Layer:
var startsWith = Expression.Lambda<Func<T, bool>>(methodExpStartsWith, parameterExp);
var equals = Expression.Lambda<Func<T, bool>>(methodExpEquals, parameterExp);
However, in order for this to work, you are missing one more PropertyExpression.
Your query now looks like:
(Layer)x => x.Hierarchy.StartsWith(...)
When, what you want is this:
(T)x => x.Layer.Hierarchy.StartsWith(...)
So, use this instead:
var parameterExp = Expression.Parameter(typeof(T), "item");
var layerExp = Expression.Property(parameterExp, "Layer");
var propertyExp = Expression.Property(layerExp, "Hierarchy");
Your logic should change a little though, since two .Where will generate an AND condition between them, and it seems like you want one of them to be true (StartsWith or Equals), so:
var parameterExp = Expression.Parameter(typeof(T), "item");
var layerExp = Expression.Property(parameterExp, "Layer");
var propertyExp = Expression.Property(layerExp, "Hierarchy");
// StartWith method
MethodInfo methodStartsWith = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
var valueStartsWith = Expression.Constant(string.Concat(hierarchy, "|"), typeof(string));
var methodExpStartsWith = Expression.Call(propertyExp, methodStartsWith, valueStartsWith);
// Equals method
MethodInfo methodEquals = typeof(string).GetMethod("Equals", new[] { typeof(string) });
var valueEquals = Expression.Constant(hierarchy, typeof(string));
var methodExpEquals = Expression.Call(propertyExp, methodEquals, valueEquals);
var orElseExp = Expression.OrElse(methodExpStartsWith, methodExpEquals);
var orElse = Expression.Lambda<Func<T, bool>>(orElseExp, parameterExp);
return query.Where(orElse);

OrderBy with SwitchExpression in Linq to entities

I need make custom orderby for enum. I try use SwitchExpression:
public static IQueryable<T> MyOrderByEnum<T>(this IQueryable<T> source, string propName, Type enumType)
{
var type = typeof (T);
var property = type.GetProperty(propName);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.Property(parameter, property);
var enumValues = Enum.GetValues(enumType);
var switchCases = new SwitchCase[enumValues.Length];
int i = 0;
foreach (var val in enumValues)
{
switchCases[i] = Expression.SwitchCase(
Expression.Constant(val.ToDisplay()),
Expression.Constant(val)
);
i++;
}
var switchExpr1 =
Expression.Switch(
propertyAccess,
Expression.Constant(""),
switchCases
);
var orderByExp1 = Expression.Lambda(switchExpr1, parameter);
MethodCallExpression resultExp = Expression.Call(typeof (Queryable), "OrderBy", new[] {type, orderByExp1.Body.Type}, source.Expression, orderByExp1);
return (IOrderedQueryable<T>) source.Provider.CreateQuery(resultExp);
}
But when I execute
filtered.MyOrderBy("field1", typeof(FieldState)).ToList();
I get error:
Unknown LINQ expression of type 'Switch'.
Is there another way to make order expression that will translate into sql construction "CASE WHEN ..."?
Try Expression.Condition (https://msdn.microsoft.com/en-us/library/bb340500%28v=vs.110%29.aspx)
I think that translates to CASE When if used in an anonymous projection

How to call SqlFunctions.PatIndex() with Expressions?

I have a business layer call that works like so:
CustomerRepository.Get(c => SqlFunctions.PatIndex("%" + arg + "%", c.FirstName));
I am trying to build this using expressions:
public virtual IEnumerable<TEntity> Like(string LikeString, string Target)
{
MethodInfo method = typeof(SqlFunctions).GetMethod("PatIndex");
var arg1 = Expression.Constant(LikeString);
var item = Expression.Parameter(typeof(TEntity), "item");
var prop = Expression.Property(item, Target);
MethodCallExpression resultExp =
Expression.Call(method, arg1, prop);
var value = Expression.Constant(0, typeof(int?));
var greaterThan = Expression.GreaterThan(resultExp, value);
var lambda = Expression.Lambda<Func<TEntity, bool>>(greaterThan, item);
var result = Repo<TEntity>.Get().AsQueryable().Where(lambda);
return result;
}
When I call the above method, I get the following exception:
This function can only be invoked from LINQ to Entities.
Any ideas of how to get past this or do what I want it to do? Does my Expression code look ok?
Figured out the issue
from:
var result = Repo<TEntity>.Get().AsQueryable().Where(lambda);
to:
var result = Repo<TEntity>.Get(lambda);

Need help creating an expression tree for Where clause

I need to create an expression tree, which I can pass along as predicate argument in Where clause to Linq To Enities query.
public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source,
Expression<Func<TSource, bool>> predicate);
Expression tree must be equivalent to where clause specified as below:
var query = context.Products.Select(product =>
new { product.Name, product.Color });
var arr = "Red;Black".Split(';');
query = query.Where(obj => arr.Contains(obj.Color));
Googled it out!
var paramExpr = Expression.Parameter(typeof(TSource), "src");
var memberExpr = (MemberExpression)property.Body;
List<Expression> arrayInits = new List<Expression>();
var arrExpr = Expression.Constant(((string)values[0]).Split(new char[] { ';' }));
MethodInfo containsMethod = typeof(ICollection<string>).GetMethod("Contains");
var containsExpression = Expression.Call(null, contains, arrExpr, memberExpr);
var containsLambda = Expression.Lambda<Func<TSource, bool>>(containsExpression, property.Parameters);
And it works!
public static Expression<Func<T, bool>> CreateContainsExpression<T>(T obj, string[] array)
{
var paramExpr = Expression.Parameter(typeof(T), "obj");
var arrExpr = Expression.Constant(array);
var colourPropExpr = Expression.Property(paramExpr, "Color");
MethodInfo containsMethod = typeof(ICollection<string>).GetMethod("Contains");
var containsExpr = Expression.Call(arrExpr, containsMethod, colourPropExpr);
return Expression.Lambda<Func<T, bool>>(containsExpr, paramExpr);
}

Categories