类型“System.Int32”的表达式不能用于方法“Boolean Equals(System.Object)”的类型为“System.Object”的参数。

14

我有一个通用的网格视图列过滤方法,可以按照列名称和搜索文本过滤网格视图记录。当我操作可空 int 数据列时,此方法会抛出错误:

表达式类型为“System.Int32”的参数不能用于方法“Boolean Equals(System.Object)”的类型为“System.Object”的参数

我的方法代码如下:

 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;
    }
}

这是我执行的查询:

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();

下面是我包含的query.GetType().ToString()结果,以更好地理解我操作的列的类型。

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]]

哪一行出现了异常? - thumbmunkeys
从group_id的body行中返回更新的问题。 - Shalin Gajjar
2个回答

16

编辑

这个问题中找到了解决方案。 在调用Equals(object)方法之前,您需要将表达式转换为Object

var converted = Expression.Convert(searchExpression, typeof(object));
body = Expression.Call(propertyExpression, containsMethod, converted);
Nicodemus13的建议是,最初将searchExpression的类型明确设置为Object应该也可以起作用。
我还没有找到问题所在,但我已经使用Linqpad在一个SSCCE中复现了这个问题。
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;}}

这一行:

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

返回一个MethodInfo,用于方法Int32?.Equals(Object other)。注意参数类型是object,而不是您可能期望的Int32(或Int32?)。

原因是typeof(Int32?)System.Nullable<Int32>,它只有Equals(object)方法。


这是有效的。但在FileSize、TotalFileSize等情况下,我从未遇到过类似的问题。 - Shalin Gajjar
我认为这是因为它们实际上不会出现在您的查询中,因此相应的case语句从未被执行。 - Rik
如果我们使用Int64工作,是否会遇到将其转换为对象的问题? - Shalin Gajjar
是的,Int64?会像Int32?一样行为,因此您需要再次将表达式转换为类型Object - Rik
1
因为您的查询中没有 FileSizeTotalFileSize 属性。在处理它们的 case 中设置一个断点,我敢打赌当您运行它时它不会被触发,因此也不会抛出错误。 - Rik

3
在LinqPad中测试时,我认为问题出在以下代码段:
searchExpression = Expression.Constant(_int1);
当你调用以下代码时:
containsMethod = typeof(Int32?).GetMethod("Equals", new[] { typeof(Int32?) });
你尝试调用的Equals方法是object.Equals(object),编译器告诉你int?类型不是该方法期望的object类型。
最简单的修复方法(虽然我不确定整个代码是否可行,但此特定错误将消失)是更改您调用的Expression.Constant的重载,以指定Equals期望的类型:
searchExpression = Expression.Constant(_int1, typeof(object));
这将编译-然而,有几件事情需要注意。
你原来的Expression.Constant(_int1)会导致Type为int而不是int?的ConstantExpression。如果需要nullable类型,则需要指定(Expression.Constant(_int1,typeof(int?)))。无论如何,你都需要将其强制转换为对象,如上所述。
指定containsMethod = typeof(Int32?).GetMethod("Equals", new[] { typeof(Int32?) })也不应起作用,因为没有这样的方法int?.Equals(int?),Equals方法是System.Object类上的方法覆盖,它接受一个对象参数并且是问题的根源。你可能要使用typeof(object).GetMethod("Equals", new[] { typeof(object) }),因为这是正确的声明。
正如我所说,它应该使用object进行编译,无论代码是否符合你的期望,我不确定,但我认为它是可以的。我期待看到它是否起作用 :)

谢谢,它有效了。但是我想知道,对于Int64类型,我是否不需要使用这种技术? - Shalin Gajjar
你是说用完全相同的代码,只是将 Int32? 替换为 Int64? 就不会抛出原始异常了? - nicodemus13
不替换意味着我已经运行良好的Int64?为什么使用Int32会出现错误? - Shalin Gajjar
通过 :) 我不知道;我看不出为什么它可以与一个工作而另一个不能。你的原始代码不应该使用 Int64? - nicodemus13
我知道。因为我认为如果我们使用bigint,Int64?可以起作用。而且它不需要转换为对象。 - Shalin Gajjar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接