4

首先,对不起我的英语不好,不是我的母语。

我在工作中的 ASP.NET MVC 项目上使用 jqgrid,并且在实现搜索时遇到了一些问题。我尝试使用一种使用 LinqExtensions 在 Internet 上找到的解决方案。当实体具有可为空的值时会出现问题,例如:

public class MyClass()
{
   string StringValue { get; set; }
   int? IntegerValue { get; set; }
}

这是因为在数据库中值接受 null,并且出于项目的其他原因需要我的 c# 代码上的可为空值。

在另一个名为 LinqExtensions 的类中,where 子句是这样的:

public static IQueryable<T> Where<T>(this IQueryable<T> source, string searchProperty, string searchString, string searchOper)
{
        Type type = typeof(T);

        if (string.IsNullOrEmpty(searchString))
            return source;

        ConstantExpression searchFilter = Expression.Constant(searchString.ToUpper());

        ParameterExpression parameter = Expression.Parameter(type, "p");
        //PropertyInfo property = type.GetProperty(searchProperty);
        //Expression propertyAccess = Expression.MakeMemberAccess(parameter, property);

        MemberExpression memberAccess = null;
        String[] separador = {"__"};
        foreach (var property2 in searchProperty.Split(separador, StringSplitOptions.None))
            memberAccess = MemberExpression.Property
               (memberAccess ?? (parameter as Expression), property2);
        Expression propertyAccess = memberAccess;

        if (propertyAccess.Type == typeof(Nullable<DateTime>))
        {
            PropertyInfo valProp = typeof(Nullable<DateTime>).GetProperty("Value");
            propertyAccess = Expression.MakeMemberAccess(propertyAccess, valProp);
            Nullable<DateTime> tn = DateTime.Parse(searchString);
            searchFilter = Expression.Constant(tn);
        }


        //support int?
        if (propertyAccess.Type == typeof(Nullable<Char>))
        {
            PropertyInfo valProp = typeof(Nullable<Char>).GetProperty("Value");
            propertyAccess = Expression.MakeMemberAccess(propertyAccess, valProp);
            Nullable<Char> tn = Char.Parse(searchString);
            searchFilter = Expression.Constant(tn);
        }

        if (propertyAccess.Type == typeof(Nullable<Int16>))
        {
            PropertyInfo valProp = typeof(Nullable<Int16>).GetProperty("Value");
            propertyAccess = Expression.MakeMemberAccess(propertyAccess, valProp);
            Nullable<Int16> tn = Int16.Parse(searchString);
            searchFilter = Expression.Constant(tn);
        }

        if (propertyAccess.Type == typeof(Nullable<Int32>))
        {
            PropertyInfo valProp = typeof(Nullable<Int32>).GetProperty("Value");
            propertyAccess = Expression.MakeMemberAccess(propertyAccess, valProp);
            Nullable<Int32> tn = Int32.Parse(searchString);
            searchFilter = Expression.Constant(tn);
        }

        if (propertyAccess.Type == typeof(Nullable<Int64>))
        {
            PropertyInfo valProp = typeof(Nullable<Int64>).GetProperty("Value");
            propertyAccess = Expression.MakeMemberAccess(propertyAccess, valProp);
            Nullable<Int64> tn = Int64.Parse(searchString);
            searchFilter = Expression.Constant(tn);
        }

        //support decimal?
        if (propertyAccess.Type == typeof(Nullable<decimal>))
        {
            PropertyInfo valProp = typeof(Nullable<decimal>).GetProperty("Value");
            propertyAccess = Expression.MakeMemberAccess(propertyAccess, valProp);

            Nullable<decimal> tn = Decimal.Parse(searchString);
            searchFilter = Expression.Constant(tn);
        }

        if (propertyAccess.Type == typeof(Char))
            searchFilter = Expression.Constant(Char.Parse(searchString));

        if (propertyAccess.Type == typeof(Int16))
            searchFilter = Expression.Constant(Int16.Parse(searchString));

        if (propertyAccess.Type == typeof(Int32))
            searchFilter = Expression.Constant(Int32.Parse(searchString));

        if (propertyAccess.Type == typeof(Int64))
            searchFilter = Expression.Constant(Int64.Parse(searchString));

        if (propertyAccess.Type == typeof(decimal))
            searchFilter = Expression.Constant(Decimal.Parse(searchString));

        if (propertyAccess.Type == typeof(DateTime))
            searchFilter = Expression.Constant(DateTime.Parse(searchString));


        MethodInfo startsWith = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
        MethodInfo endsWith = typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) });
        MethodInfo contains = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });

        //MethodInfo contains = typeof(Int32Extensions).GetMethod("Contains", new Type[] { typeof(Int64), typeof(Int64) });

        Expression operation = null;

        switch (searchOper)
        {
            default:
            case "eq":
                operation = Expression.Equal(propertyAccess, searchFilter);
                break;
            case "ne":
                operation = Expression.NotEqual(propertyAccess, searchFilter);
                break;
            case "lt":
                operation = Expression.LessThan(propertyAccess, searchFilter);
                break;
            case "le":
                operation = Expression.LessThanOrEqual(propertyAccess, searchFilter);
                break;
            case "gt":
                operation = Expression.GreaterThan(propertyAccess, searchFilter);
                break;
            case "ge":
                operation = Expression.GreaterThanOrEqual(propertyAccess, searchFilter);
                break;
            case "bw":
                operation = Expression.Call(propertyAccess, startsWith, searchFilter);
                break;
            case "bn":
                operation = Expression.Call(propertyAccess, startsWith, searchFilter);
                operation = Expression.Not(operation);
                break;
            case "ew":
                operation = Expression.Call(propertyAccess, endsWith, searchFilter);
                break;
            case "en":
                operation = Expression.Call(propertyAccess, endsWith, searchFilter);
                operation = Expression.Not(operation);
                break;
            case "cn":
                operation = Expression.Call(propertyAccess, contains, searchFilter);
                break;
            case "nc":
                operation = Expression.Call(propertyAccess, contains, searchFilter);
                operation = Expression.Not(operation);
                break;
        }

        var whereExpression = Expression.Lambda(operation, parameter);

        var resultExpression = Expression.Call(typeof(Queryable), "Where", new Type[] { source.ElementType }, source.Expression, whereExpression);

        return source.Provider.CreateQuery<T>(resultExpression);
    }

Model类中的方法是:

public JsonResult GetData(GridSettings grid)
    {
        if (Session["SomeValue"] != null)
        {
            var query = (new GridModel()).GetQuery();

            //Filters
            if (grid.IsSearch && grid.Where != null)
            {
                //And
                if (grid.Where.groupOp == "AND")
                    foreach (var rule in grid.Where.rules)
                        query = query.Where<MyClass>(rule.field, rule.data.ToUpper(), rule.op);
                else
                {
                    //Or
                    var temp = (new List<MyClass>()).AsQueryable();
                    foreach (var rule in grid.Where.rules)
                    {
                        var t = query.Where<MyClass>(rule.field, rule.data, rule.op);
                        temp = temp.Concat<MyClass>(t);
                    }
                    //Clean repeat elements
                    query = temp.Distinct<MyClass>();
                }
            }
            //Order
            query = query.OrderBy<MyClass>(grid.SortColumn,
                grid.SortOrder);
            //Count
            var count = query.Count();
            //Pager
            var data = query.Skip((grid.PageIndex - 1) * grid.PageSize).Take(grid.PageSize).ToArray();

            //Convert
            var result = new
            {
                .
                .
                .
            }
  }

我创建了网格,非常正确地显示值但是......当通过字符串搜索时,即使某些值为空,并且如果尝试通过 IntegerValue 搜索(支持空)并且当实体上的某些值为空时抛出异常. 这个问题正在把我变成一个疯子。

请,如果有人有同样的问题或知道如何解决它,我将永远感激

再见

4

1 回答 1

5

它比我想象的更容易,最好的方法是覆盖调用表达式的运算符:

例如在我的问题中,将 equals 方法重写为:

    private static Expression LinqEqual(Expression e1, Expression e2)
    {
        if (IsNullableType(e1.Type) && !IsNullableType(e2.Type))
            e2 = Expression.Convert(e2, e1.Type);
        else if (!IsNullableType(e1.Type) && IsNullableType(e2.Type))
            e1 = Expression.Convert(e1, e2.Type);
        return Expression.Equal(e1, e2);
    }

    private static bool IsNullableType(Type t)
    {
        return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>);
    }

并替换一段代码:

Expression operation = null;

    switch (searchOper)
    {
        default:
        case "eq":
            operation = Expression.Equal(propertyAccess, searchFilter);
            break;
        .
        .
        .
    }

我希望问题和解决方案对某人有所帮助。

感谢 Stackoverflow

于 2011-06-13T22:32:15.643 回答