Answered by:
EF Dynamic Query - Like

Question
-
After successfully implementing a like function with use of string function 'contains' I noticed that wildcards are escaped when translated to sql.
Eg. When I search for a street starting with p and has number 13 in it. I replace all space with '%' but EF escapes it. The output query then looks like this:
SELECT " FROM Customer WHERE (LOWER([Street]) LIKE N'%p~%13%' ESCAPE N'~')
In one of the blogs a suggestion was to use patindex instead. I just don't know how to implement this.
My current code looks like this:
public static Expression<Func<T, bool>> Create<T>(string propertyName, ComparisonOperators comparisonOperator, dynamic comparedValue1, dynamic comparedValue2 = null) { ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "x"); MemberExpression memberExpression = Expression.MakeMemberAccess(parameterExpression, typeof(T).GetProperty(propertyName)); ConstantExpression constantExpression = Expression.Constant(comparedValue1, comparedValue1.GetType()); Expression expressionBody = null; switch (comparisonOperator) { ... case ComparisonOperators.Contains: expressionBody = Expression.Call(GetLowerCasePropertyAccess(memberExpression), ContainsMethod, Expression.Constant(comparedValue1.ToLower())); break; } return Expression.Lambda<Func<T, bool>>(expressionBody, new ParameterExpression[] { parameterExpression }); } private static MethodCallExpression GetLowerCasePropertyAccess(MemberExpression propertyAccess) { var stringExpression = GetConvertToStringExpression(propertyAccess); if (stringExpression == null) throw new Exception(string.Format("Not supported property type {0}", propertyAccess.Type)); return Expression.Call(stringExpression, typeof(string).GetMethod("ToLower", Type.EmptyTypes)); } private static Expression GetConvertToStringExpression(Expression e) { // if property string - no cast needed // else - use SqlFunction.StringConvert(double?) or SqlFunction.StringConvert(decimal?); Expression strExpression = null; if (e.Type == typeof(string)) strExpression = e; else { var systemType = Nullable.GetUnderlyingType(e.Type) ?? e.Type; if (systemType == typeof(int) || systemType == typeof(long) || systemType == typeof(double) || systemType == typeof(short) || systemType == typeof(byte)) { // cast int to double var doubleExpr = Expression.Convert(e, typeof(double?)); strExpression = Expression.Call(StringConvertMethodDouble, doubleExpr); } else if (systemType == typeof(decimal)) { // call decimal version of StringConvert method // cast to nullable decimal var decimalExpr = Expression.Convert(e, typeof(decimal?)); strExpression = Expression.Call(StringConvertMethodDecimal, decimalExpr); } } return strExpression; } private static readonly MethodInfo ContainsMethod = typeof(String).GetMethod("Contains", new Type[] { typeof(String) }); private static readonly MethodInfo StringConvertMethodDouble = typeof(SqlFunctions).GetMethod("StringConvert", new Type[] { typeof(double?) }); private static readonly MethodInfo StringConvertMethodDecimal = typeof(SqlFunctions).GetMethod("StringConvert", new Type[] { typeof(decimal?) });
I need to replace the method call to use patindex instead:
expressionBody = Expression.Call(GetLowerCasePropertyAccess(memberExpression), ContainsMethod, Expression.Constant(comparedValue1.ToLower()));
- Edited by Syslock Thursday, September 5, 2013 7:33 AM
- Moved by Lilia gong - MSFT Friday, September 6, 2013 1:32 AM Data Platform
Thursday, September 5, 2013 6:20 AM
Answers
-
Hi,
Maybe codes like below is what you want:
static void Main(string[] args) { using (DataClasses1DataContext dataContext = new DataClasses1DataContext()) { var productNames = new string[] { "P", "13" }; IQueryable<Course> custs = dataContext.Courses; IQueryable<Course> query = dataContext.Courses.AsQueryable().Provider.CreateQuery<Course>(likeExpression(custs, productNames)); } } private static Expression likeExpression(IQueryable<Course> custs, string[] productNames) { ParameterExpression param = Expression.Parameter(typeof(Course), "c"); //c.City.StartsWith("P") Expression left = Expression.Property(param, typeof(Course).GetProperty("CourseName")); Expression right1 = Expression.Constant("P"); Expression right2 = Expression.Constant("13"); MethodCallExpression filter1 = Expression.Call(left, typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }), right1); MethodCallExpression filter2 = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), right2); LambdaExpression pred = Expression.Lambda(Expression.And(filter1, filter2), param); //Where(c=>c.City.StartsWith("London")) MethodCallExpression whereCallExpression = Expression.Call( typeof(Queryable), "Where", new Type[] { typeof(Course) }, Expression.Constant(custs), pred); return whereCallExpression; }
More information about how to use Expression Trees to Build Dynamic Queries:
http://msdn.microsoft.com/en-us/library/vstudio/bb882637.aspx
Regards.
<THE CONTENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED>
Thanks
MSDN Community Support
Please remember to "Mark as Answer" the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.Wednesday, September 11, 2013 12:51 PM
All replies
-
Hi Syslock,
Thanks for your posting.
From your description, I think this issue is related to EF Dynamic Query. So Data Platform Development forums is more suitable for it, I move it to Data Platform Development forums.
Data Platform Development forums : http://social.msdn.microsoft.com/Forums/en-US/home?category=dataplatformdevHope these help.
Lilia Gong <THE CONTENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED>
Thanks
MSDN Community Support
Please remember to "Mark as Answer" the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.Friday, September 6, 2013 1:31 AM -
Hello,
In my opinion, to implement the search for a street starting with p and has number 13 in it we can use Startwith and Contans() like below:
var result = from p in db.Persons where p.Address.StartsWith("P") & p.Address.Contains("13") select p;
Have a try.
Regards.
<THE CONTENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED>
Thanks
MSDN Community Support
Please remember to "Mark as Answer" the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.Friday, September 6, 2013 3:59 AM -
Hi
I know how to construct the predicate but why I need is how to create the Expression like the above.
Wednesday, September 11, 2013 6:18 AM -
Hi,
Maybe codes like below is what you want:
static void Main(string[] args) { using (DataClasses1DataContext dataContext = new DataClasses1DataContext()) { var productNames = new string[] { "P", "13" }; IQueryable<Course> custs = dataContext.Courses; IQueryable<Course> query = dataContext.Courses.AsQueryable().Provider.CreateQuery<Course>(likeExpression(custs, productNames)); } } private static Expression likeExpression(IQueryable<Course> custs, string[] productNames) { ParameterExpression param = Expression.Parameter(typeof(Course), "c"); //c.City.StartsWith("P") Expression left = Expression.Property(param, typeof(Course).GetProperty("CourseName")); Expression right1 = Expression.Constant("P"); Expression right2 = Expression.Constant("13"); MethodCallExpression filter1 = Expression.Call(left, typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }), right1); MethodCallExpression filter2 = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), right2); LambdaExpression pred = Expression.Lambda(Expression.And(filter1, filter2), param); //Where(c=>c.City.StartsWith("London")) MethodCallExpression whereCallExpression = Expression.Call( typeof(Queryable), "Where", new Type[] { typeof(Course) }, Expression.Constant(custs), pred); return whereCallExpression; }
More information about how to use Expression Trees to Build Dynamic Queries:
http://msdn.microsoft.com/en-us/library/vstudio/bb882637.aspx
Regards.
<THE CONTENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED>
Thanks
MSDN Community Support
Please remember to "Mark as Answer" the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.Wednesday, September 11, 2013 12:51 PM