关于如何在linq中优化字符串排序代码的建议

时间:2014-01-30 13:08:39

标签: c# linq linq-to-sql

我正在为WebGrid编写一个IQueryable友好替代品,我停止了我必须通过作为字符串传递的列名来命令结果的部分。我设法实现了我的目标,但我不喜欢结果代码所以我希望有人可以给我一个提示如何改进它。

我为IQueryable编写了一个扩展名,用于根据我找到的here按字符串排序,但是它最初没有用,并且给了我一个错误,说明(或多或少)Int32无法转换为对象(此时我正在使用一列id来测试列名称的排序,这就是错误说Int32的原因)。有问题的一行:

var mySortExpression = Expression.Lambda<Func<T, object>>(Expression.Property(param, columnName), param);

我添加了一个基于this page可以找到的转换,但是这也没有用,我得到一个错误,说我不能按对象类排序。以下是它如何看待这一点:

var mySortExpression = Expression.Lambda<Func<T, object>>(Expression.Convert(Expression.Property(param, columnName), type), param);

所以我发现我需要在这一行中提供一个可排序的类型:

var mySortExpression = Expression.Lambda<Func<T, type>>(Expression.Property(param, columnName), param);

并将整个事情重写如下:

    public static IQueryable<T> OrderByString<T>(this IQueryable<T> query, string columnName)
    {
        var elementType = typeof(T);
        var param = Expression.Parameter(elementType, "x");

        var prop = elementType.GetProperty(columnName);
        Type type = Nullable.GetUnderlyingType(prop.PropertyType);
        if (type == null)
        {
            type = prop.PropertyType;
        }

        if (type.Equals(typeof(string)))
        {
            var mySortExpression = Expression.Lambda<Func<T, string>>(Expression.Property(param, columnName), param);
            return query.OrderBy(mySortExpression);
        }
        if (type.Equals(typeof(char)))
        {
            var mySortExpression = Expression.Lambda<Func<T, char>>(Expression.Property(param, columnName), param);
            return query.OrderBy(mySortExpression);
        }
        if (type.Equals(typeof(int)))
        {
            var mySortExpression = Expression.Lambda<Func<T, int>>(Expression.Property(param, columnName), param);
            return query.OrderBy(mySortExpression);
        }
        if (type.Equals(typeof(float)) || type.Equals(typeof(double)))
        {
            var mySortExpression = Expression.Lambda<Func<T, double>>(Expression.Property(param, columnName), param);
            return query.OrderBy(mySortExpression);
        }
        // This last part won't work but I left it so that it can compile (all routes need to return value etc.)
        var mySortExpression1 = Expression.Lambda<Func<T, object>>(Expression.Convert(Expression.Property(param, columnName), type), param);

        return query.OrderBy(mySortExpression1);

    }

这实际上有效,但所有这些重复看起来都不太好。有没有办法改进这段代码?

1 个答案:

答案 0 :(得分:0)

正如RaphaëlAlthaus所建议的那样,我使用https://stackoverflow.com/a/233505/2123652中的代码解决了这个问题,但是我应用了一个小改动来允许它正确处理接口。结果代码如下所示:

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");
}
static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName) {
    string[] props = property.Split('.');
    Type elementType = source.ElementType;
    Type type = elementType;
    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(elementType, 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(elementType, type)
            .Invoke(null, new object[] {source, lambda});
    return (IOrderedQueryable<T>)result;
}

colde的变化涉及用typeof(T)替换source.ElementType