Expression of type 'System.Int32' cannot be used for parameter of type 'System.Object' of method 'Boolean Equals(System.Object)'

22,269

Solution 1

EDIT

Found the solution in this question. You need to convert the expression to Object before calling the Equals(object)method:

var converted = Expression.Convert(searchExpression, typeof(object));
body = Expression.Call(propertyExpression, containsMethod, converted);

Nicodemus13's suggestion of explicitly setting searchExpression's type to Object in the first place should work, too.

Original

I haven't found the issue yet, but I have reproduced the problem in a SSCCE using Linqpad:

void Main()
{
    var myInstance = new myClass();
    var equalsMethod = typeof(Int32?).GetMethod("Equals", new[] { typeof(Int32?) });
    int? nullableInt = 1;
    var nullableIntExpr = System.Linq.Expressions.Expression.Constant(nullableInt);
    var myInstanceExpr = System.Linq.Expressions.Expression.Constant(myInstance);
    var propertyExpr = System.Linq.Expressions.Expression.Property(myInstanceExpr, "MyProperty");
    var result = Expression.Call(propertyExpr,equalsMethod,nullableIntExpr); // This line throws the exception.
    Console.WriteLine(result);
}

class myClass{public int? MyProperty{get;set;}}

This line:

containsMethod = typeof(Int32?).GetMethod("Equals", new[] { typeof(Int32?) });

returns a MethodInfo for the method Int32?.Equals (Object other). Notice the parameter type is object, not Int32 (or Int32?) as you might expect.

The reason is typeof(Int32?) is System.Nullable<Int32>, which only has the Equals(object) method.

Solution 2

Playing around with this in LinqPad, I think the problem is around:

searchExpression = Expression.Constant(_int1);

when you call:

containsMethod = typeof(Int32?).GetMethod("Equals", new[] { typeof(Int32?) });

The Equals method you're attempting to call is object.Equals(object) and the compiler is telling you that the type int? is not the type object that the method expects.

The simplest fix (though I'm not sure that the overall code will work, though this particular error will go away) is to change the overload of the Expression.Constant that you call to one that specifies the type that Equals expects:

searchExpression = Expression.Constant(_int1, typeof(object));

This will compile- however, there's a few things to note.

  1. Your original Expression.Constant(_int1) results in a ConstantExpression with Type int not int?. You'd need to specify the nullable type, if you needed that (Expression.Constant(_int1, typeof(int?))). However, you'll need to cast it to object anyway, as above.

  2. Specifying containsMethod = typeof(Int32?).GetMethod("Equals", new[] { typeof(Int32?) }); shouldn't work anyway, as there's not such method int?.Equals(int?), the Equals method is the override of the method on the System.Object class which takes an object parameter and is the root of the problem. You may as well use: typeof(object).GetMethod("Equals", new[] { typeof(object) }); as that's the correct declaration.

As I said, it should compile with object, whether the code does what you expect, I'm not sure, but I think so. I look forward to seeing whether it works :)

Share:
22,269
Shalin Gajjar
Author by

Shalin Gajjar

Updated on August 04, 2022

Comments

  • Shalin Gajjar
    Shalin Gajjar almost 2 years

    i have one common grid view column filter method that filter grid view record with ColumnName and SearchText wise. here when i operate on nullable int datacolumn there is error thrown from this method like :

    Expression of type 'System.Int32' cannot be used for parameter of type 'System.Object' of method 'Boolean Equals(System.Object)'

    my method code is :

     public static IQueryable<T> FilterForColumn<T>(this IQueryable<T> queryable, string colName, string searchText)
    {
        if (colName != null && searchText != null)
        {
            var parameter = Expression.Parameter(typeof(T), "m");
            var propertyExpression = Expression.Property(parameter, colName);
            System.Linq.Expressions.ConstantExpression searchExpression = null;
            System.Reflection.MethodInfo containsMethod = null;
            // this must be of type Expression to accept different type of expressions
            // i.e. BinaryExpression, MethodCallExpression, ...
            System.Linq.Expressions.Expression body = null;
            Expression ex1 = null;
            Expression ex2 = null;
            switch (colName)
            {
                case "JobID":
                case "status_id":
                    Int32 _int = Convert.ToInt32(searchText);
                    searchExpression = Expression.Constant(_int);
                    containsMethod = typeof(Int32).GetMethod("Equals", new[] { typeof(Int32) });
                    body = Expression.Call(propertyExpression, containsMethod, searchExpression);
                    break;
                case "group_id":
                    Int32? _int1 = Convert.ToInt32(searchText);
                    searchExpression = Expression.Constant(_int1);
                    containsMethod = typeof(Int32?).GetMethod("Equals", new[] { typeof(Int32?) });                     
                    //Error throws from this line
                    body = Expression.Call(propertyExpression, containsMethod, searchExpression);
    
    
                    break;
                case "FileSize":
                case "TotalFileSize":
                    Int64? _int2 = Convert.ToInt64(searchText);
                    searchExpression = Expression.Constant(_int2);
                    containsMethod = typeof(Int64?).GetMethod("Equals", new[] { typeof(Int64?) });
                    body = Expression.Call(propertyExpression, containsMethod, searchExpression);
                    break;
                // section for DateTime? properties
                case "PublishDate":
                case "Birth_date":
                case "Anniversary_date":
                case "Profile_Updated_datetime":
                case "CompletedOn":
                    DateTime currentDate = DateTime.ParseExact(searchText, "dd/MM/yyyy", null);
                    DateTime nextDate = currentDate.AddDays(1);
                    ex1 = Expression.GreaterThanOrEqual(propertyExpression, Expression.Constant(currentDate, typeof(DateTime?)));
                    ex2 = Expression.LessThan(propertyExpression, Expression.Constant(nextDate, typeof(DateTime?)));
                    body = Expression.AndAlso(ex1, ex2);
                    break;
                // section for DateTime properties
                case "Created_datetime":
                case "Reminder_Date":
                case "News_date":
                case "thought_date":
                case "SubscriptionDateTime":
                case "Register_datetime":
                case "CreatedOn":
                    DateTime currentDate1 = DateTime.ParseExact(searchText, "dd/MM/yyyy", null);
                    DateTime nextDate1 = currentDate1.AddDays(1);
                    ex1 = Expression.GreaterThanOrEqual(propertyExpression, Expression.Constant(currentDate1));
                    ex2 = Expression.LessThan(propertyExpression, Expression.Constant(nextDate1));
                    body = Expression.AndAlso(ex1, ex2);
                    break;
                default:
                    searchExpression = Expression.Constant(searchText);
                    containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
                    body = Expression.Call(propertyExpression, containsMethod, searchExpression);
                    break;
            }
            var predicate = Expression.Lambda<Func<T, bool>>(body, new[] { parameter });
            return queryable.Where(predicate);
        }
        else
        {
            return queryable;
        }
    }
    

    here is my query that i fired :

    var query = Helper.GetUsers().Where(u => u.Id != user_id).OrderByDescending(u => u.Register_datetime).Select(u => new
                      {
                          Id = u.Id,
                          Name = u.First_name + " " + u.Last_name,
                          IsActive = u.IsActive,
                          IsVerified = u.IsVerified,
                          Username = u.Username,
                          password = u.password,
                          Birth_date = u.Birth_date,
                          Anniversary_date = u.Anniversary_date,
                          status_id = u.status_id,
                          group_id = u.group_id,
                          Profile_Updated_datetime = u.Profile_Updated_datetime,
                          Register_datetime = u.Register_datetime
                      }).FilterForColumn(ColumnName, SearchText).ToList();
    

    here i include my query.GetType().ToString() result for better understanding types of columns that i operate on it.

    System.Collections.Generic.List`1[<>f__AnonymousType0`12[System.Int32,System.String,System.Boolean,System.Boolean,System.String,System.String,System.Nullable`1[System.DateTime],System.Nullable`1[System.DateTime],System.Int32,System.Nullable`1[System.Int32],System.Nullable`1[System.DateTime],System.DateTime]]
    
  • Shalin Gajjar
    Shalin Gajjar about 10 years
    thank u..it's works. but how ever i wonder i don't need this technique used for Int64?.
  • Shalin Gajjar
    Shalin Gajjar about 10 years
    this works. but how ever at cases of case FileSize,TotalFileSize i never found any problem like this.
  • Rik
    Rik about 10 years
    I think that's because those don't actually appear in your query, so the corresponding case statement is never executed.
  • nicodemus13
    nicodemus13 about 10 years
    You're saying that (your) exactly the same code, but replacing Int32? with Int64? doesn't throw the original exception?
  • Shalin Gajjar
    Shalin Gajjar about 10 years
    not replacing means i have already Int64? which is running fine. why Int32? getting error like that.
  • Shalin Gajjar
    Shalin Gajjar about 10 years
    Can we face issue like converting to object in case we work with Int64?.
  • nicodemus13
    nicodemus13 about 10 years
    Pass :) I don't know; I can't see why it would work with one and not the other. Your original code shouldn't work with Int64?
  • Shalin Gajjar
    Shalin Gajjar about 10 years
    ya i know. Because i think Int64? works with if we are working with bigint. And it is not required for casting to object.
  • Rik
    Rik about 10 years
    Yes, the Int64?will behave the same way as Int32?, so you would need to convert the expression to type Object again.
  • Rik
    Rik about 10 years
    Because there's no FileSize or TotalFileSize property in your query. Put a breakpoint in the case handling them, and I bet it will not get hit when you run it, so it won't throw an error.