none
Entityframework - dynamic query RRS feed

  • Question

  • Hi

    I'm creating a dynamic expression builder and trying to implement the 'like' function. Before writing my own i've searched for any existing function and found one close to my need. After several experimentations i couldn't bring it to run for types other than string.

    When i pass a parameter of type int then i get this error:

    Method 'System.String ToString()' declared on type 'System.String' cannot be called with instance of type 'System.Int32'

    My code looks like this:

    private static MethodCallExpression GetLowerCasePropertyAccess(MemberExpression propertyAccess)
    {
    	//return Expression.Call(Expression.Call(propertyAccess, "ToString", new Type[0]), typeof(string).GetMethod("ToLower", new Type[0]));
    	return Expression.Call(Expression.Call(propertyAccess, typeof(string).GetMethod("ToString", System.Type.EmptyTypes)), typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
    }
    
    private static readonly MethodInfo ContainsMethod = typeof(String).GetMethod("Contains", new Type[] { typeof(String) });
    
    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:
    			//var indexOf = Expression.Call(memberExpression, "IndexOf", null, Expression.Constant(comparedValue1, typeof(string)), Expression.Constant(StringComparison.InvariantCultureIgnoreCase));
    			//expressionBody = Expression.GreaterThanOrEqual(indexOf, Expression.Constant(0));
    			expressionBody = Expression.Call(GetLowerCasePropertyAccess(memberExpression), ContainsMethod, Expression.Constant(comparedValue1.ToLower()));
    			break;
    	}
    
    	return Expression.Lambda<Func<T, bool>>(expressionBody, new ParameterExpression[] { parameterExpression });
    }

    • Moved by Ionut Duma Monday, September 2, 2013 2:36 PM Wrong language
    Sunday, September 1, 2013 12:26 PM

Answers

  • Hi

    Thanks for the reply. Someone on stackoverflow board posted the correct solution but yours is going in that direction so I've voted it as helpful.

    Here's the posted solution:

    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;
    
        var systemType = Nullable.GetUnderlyingType(e.Type) ?? e.Type;
    
        if (systemType == typeof(int) 
            || systemType == typeof(long) 
            || systemType == typeof(double)
            || systemType == typeof(short)
            || systemType == typeof(byte)) // continue
        {
            // cast int to double
            var doubleExpr = Expression.Convert(e, typeof (double?));
            strExpression = Expression.Call(StringConvertMethodDouble, doubleExpr);
        }
    
        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 MethodCallExpression GetLowerCasePropertyAccess(Expression 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 readonly MethodInfo StringConvertMethodDouble = typeof (SqlFunctions).GetMethod("StringConvert",
        new Type[] {typeof (double?)});
    private static readonly MethodInfo StringConvertMethodDecimal = typeof(SqlFunctions).GetMethod("StringConvert",
        new Type[] { typeof(decimal?) });

     
    • Marked as answer by Syslock Thursday, September 5, 2013 6:17 AM
    Thursday, September 5, 2013 6:17 AM

All replies

  • Hi Syslock,

    This issue was caused by code below:

      return Expression.Call(Expression.Call(propertyAccess,

                         typeof(string).GetMethod("ToString", System.Type.EmptyTypes)),

                         typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));

    When you inputted propertyAccess instance as Int32 type, the error throwed. My solution is determine the instance’s type and choose the right method to call.

    Some code for you to try:

    private static MethodCallExpression GetLowerCasePropertyAccess(MemberExpression propertyAccess)

            {

               //directly use Object Tostring method

                return Expression.Call(Expression.Call(propertyAccess,

                         typeof(Object).GetMethod("ToString", System.Type.EmptyTypes)),

                         typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));

            }

    public class Person

        {

            public string Name { get; set; }

            public int Age { get; set; }

            public string ToLower()

            {

                return Name.ToLower();

            }

    }

    static void Main(string[] args)

            {

                Person person = new Person() { Name = "Bill Gates" ,Age=50};

                var fun = Create<Person>("Age", ComparisonOperators.Contains, person);

               

                Console.Read();

            }

    Best 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.

    Tuesday, September 3, 2013 7:48 AM
    Moderator
  • Hi

    Thanks for the reply. Someone on stackoverflow board posted the correct solution but yours is going in that direction so I've voted it as helpful.

    Here's the posted solution:

    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;
    
        var systemType = Nullable.GetUnderlyingType(e.Type) ?? e.Type;
    
        if (systemType == typeof(int) 
            || systemType == typeof(long) 
            || systemType == typeof(double)
            || systemType == typeof(short)
            || systemType == typeof(byte)) // continue
        {
            // cast int to double
            var doubleExpr = Expression.Convert(e, typeof (double?));
            strExpression = Expression.Call(StringConvertMethodDouble, doubleExpr);
        }
    
        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 MethodCallExpression GetLowerCasePropertyAccess(Expression 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 readonly MethodInfo StringConvertMethodDouble = typeof (SqlFunctions).GetMethod("StringConvert",
        new Type[] {typeof (double?)});
    private static readonly MethodInfo StringConvertMethodDecimal = typeof(SqlFunctions).GetMethod("StringConvert",
        new Type[] { typeof(decimal?) });

     
    • Marked as answer by Syslock Thursday, September 5, 2013 6:17 AM
    Thursday, September 5, 2013 6:17 AM