从字符串创建表达式(空引用)

时间:2012-02-01 08:39:13

标签: c# generics sql-order-by expression expression-trees

问题在于:

我们使用表对象来允许用户执行搜索,排序,分页等功能。这些表工作得很好。但是其中一个功能存在问题: 排序(= OrderBy)。

实际上,为了允许排序,我们在每列中设置一个表示表达式的字符串: 例如,如果表达式是Person => Person.Id,那么字符串是Id; 如果表达式是Person => Person.Address.Street,字符串是Address.Street。

在第一种情况下(Person => Person.Id),它很有用,因为它不是子对象。 但在第二种情况下(Person => Person.Address.Street),它不会,因为Address对象可能为null。

为了允许从字符串执行Orderby,我在另一篇文章中找到了以下方法:

public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
{
    return ApplyOrder<T>(source, property, "OrderBy");
}

public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
{
    return ApplyOrder<T>(source, property, "OrderByDescending");
}

public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
{
    return ApplyOrder<T>(source, property, "ThenBy");
}

public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
{
    return ApplyOrder<T>(source, property, "ThenByDescending");
}

private static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
{
    string[] props = property.Split('.');
    Type type = typeof(T);
    ParameterExpression arg = Expression.Parameter(type, "x");
    Expression expr = arg;
    foreach (string prop in props)
    {
        // use reflection (not ComponentModel) to mirror LINQ 
        PropertyInfo pi = type.GetProperty(prop);
        expr = Expression.Property(expr, pi);
        type = pi.PropertyType;
    }
    Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
    LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
    object result = typeof(Queryable).GetMethods().Single(
    method => method.Name == methodName
        && method.IsGenericMethodDefinition
        && method.GetGenericArguments().Length == 2
        && method.GetParameters().Length == 2)
            .MakeGenericMethod(typeof(T), type)
            .Invoke(null, new object[] { source, lambda });
    return (IOrderedQueryable<T>)result;
}

你们是否有任何想法允许我添加一个不会用subobject == null选择对象的条件?或者阻止它尝试从null的对象访问属性?

编辑:

检查看起来像:list.OrderBy(x =&gt;(x.Address!= null)?x.Address.Street:string.Empty)。

所以我需要在x和最终字段之间的每个对象上添加一个空检查。 是否可以使用这些方法来做到这一点?

编辑2:

我试图替换

Expression.Property(expr, pi);

通过

expr = Expression.Condition(
                Expression.Equal(expr, Expression.Constant(null)),
                Expression.Constant(String.Empty),
                Expression.Property(expr, pi));

但它似乎不起作用。我得到以下异常:

Argument types do not match

知道我应该怎么知道expr访问的字段的默认值吗?

1 个答案:

答案 0 :(得分:0)

创建null常量作为属性类型。

expr = Expression.Condition(Expression.Equal(expr, Expression.Constant(null, expr.Type)),
                Expression.Constant(String.Empty),
                Expression.Property(expr, pi));