Expression. Like in C #
for example: x => x.Name = "g"
I have a code block like this
public Expression<Func<TEntity, bool>> SearchExpression() { var c = new ConstantExpression[_paramList.Count]; var b = new BinaryExpression[_paramList.Count]; BinaryExpression comparisonExpression = null; var entity = Expression.Parameter(typeof(TEntity)); for (int i = 0; i < _paramList.Count; i++) { var value = Convert.ChangeType(_paramList[i].Item2 /*"g"*/, _paramList[i].Item3 /*System.String*/); c[i] = Expression.Constant(value); //"g" // PROBLEM IS HERE b[i] = Expression.Equal(Expression.Property(entity, _paramList[i].Item1 /*Name*/, c[i]); // PROBLEM IS HERE } _paramList.Clear(); comparisonExpression = b.Aggregate(Expression.And); return Expression.Lambda<Func<TEntity, bool>>(comparisonExpression, entity); } works like a spell, but I need Expression.Like (like "g" doesn't equal "g")
Expression.Like(Expression.Property(entity, _paramList[i].Item1), c[i]) but C # expression tree doesn't support Like method
UPDATE :
I wrote something like this:
Expression.Call(Expression.Property(entity, _paramList[i].Item1), typeof(String).GetMethod("Contains"), new Expression[] { c[i] }); but I need BinaryExpression not MethodCallExpression
You can make your code work by adding an equality expression to the method call, for example:
b[i] = Expression.Equal( Expression.Call(Expression.Property(entity, _paramList[i].Item1), typeof (String).GetMethod("Contains"), new Expression[] {c[i]}), Expression.Constant(true)); In pseudo code, it reads like:
b[i] = entity => entity.someProperty.Contains(c[i]) == true; which will return you a binary expression.
This answer does not take into account your array and "and" aggregation, but this should be considered as a separate issue.
Consider this class:
class MyEntity { string Name { get; set; } } We want to request:
select ... from MyEntity where Name like '%query%'; The following method is a general implementation of the above query template:
static Expression<Func<TEntity, bool>> Like<TEntity>(string propertyName, string queryText) { var parameter = Expression.Parameter(typeof (TEntity), "entity"); var getter = Expression.Property(parameter, propertyName); //ToString is not supported in Linq-To-Entities, throw an exception if the property is not a string. if (getter.Type != typeof (string)) throw new ArgumentException("Property must be a string"); //string.Contains with string parameter. var stringContainsMethod = typeof (string).GetMethod("Contains", new[] {typeof (string)}); var containsCall = Expression.Call(getter, stringContainsMethod, Expression.Constant(queryText, typeof (string))); return Expression.Lambda<Func<TEntity, bool>>(containsCall, parameter); } If you want to have the query% template query% or %query , you can use string.StartsWith and string.EndsWith instead of Contains .
In addition, you can share the setting for multiple calls if you configure the signature.
The current implementation throws an exception if the property data type is not a string. Have a look at this answer fooobar.com/questions/34737 / ... for converting numbers to strings.
I did this in a scripting language that I wrote, which allows me to say things like name line 'bob%' . The trick is that you need to map it to a method call that takes a value and a regular expression and calls it from Expression .
If you look at the LikeEvaluator class in my Wire language, you will see how I did this:
static class LikeEvaluator { private static readonly MethodInfo ApplyLikeMethodInfo=typeof(LikeEvaluator).GetMethod("ApplyLike"); private static readonly MethodInfo ApplyLikeNoCaseMethodInfo=typeof(LikeEvaluator).GetMethod("ApplyLikeNoCase"); public static Expression Like(CaseMode caseMode, Expression lhs, Expression pattern) { Expression x=null; if(caseMode==CaseMode.Sensitive) { x=Expression.Call(ApplyLikeMethodInfo,lhs,pattern); } else { x=Expression.Call(ApplyLikeNoCaseMethodInfo,lhs,pattern); } return x; } public static bool ApplyLike(string text, string likePattern) { string pattern=PatternToRegex(likePattern); return Regex.IsMatch(text,pattern,RegexOptions.None); } public static bool ApplyLikeNoCase(string text, string likePattern) { string pattern=PatternToRegex(likePattern); return Regex.IsMatch(text,pattern,RegexOptions.IgnoreCase); } public static string PatternToRegex(string pattern) { pattern=Regex.Escape(pattern); pattern=pattern.Replace("%",@".*"); pattern=string.Format("^{0}$",pattern); return pattern; } }