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]]
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.
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.
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.
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 :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With