无法让动态OrderBy在我的通用列表上工作

时间:2014-03-14 16:48:01

标签: c# linq

我无法让动态OrderBy在我的通用列表上工作;

var list = CacheObjects.CompetencyAssessments
                       .Select(x => new CompetencyAssessmentLineViewModel(x))
                       .ToList();

var sortInfo = string.Format("{0} {1}", request.SortingName, request.SortingOrder);

var displayList = list.AsQueryable()
                      .OrderBy(sortInfo)
                      .Skip(startIndex)
                      .Take(pageLength);

我使用字符串作为OrderBy的动态功能。 但是代码没有编译;

  

错误1方法' System.Linq.Queryable.OrderBy(System.Linq.IQueryable,System.Linq.Expressions.Expression>)的类型参数'无法从使用中推断出来。尝试显式指定类型参数。

我做错了什么?

该方法的签名是:

public JsonResult GridData(JqGridRequest request)

JqGridRequest来自NuGet包Lib.Web.Mvc。所以:

  • request.SortingName是一个包含字段名称的字符串,
  • request.SortingOrder是排序顺序

请参阅:http://tpeczek.com/2011/03/jqgrid-and-aspnet-mvc-strongly-typed.html

3 个答案:

答案 0 :(得分:3)

我怀疑你将IEnumerable和IQueryable混淆了6年前Scott Guthrie提到的Dynamic LINQ库。这是您必须添加到项目中的外部库。其最新版本于2年前发布为a NuGet package

图书馆有一些限制,去年另一个System.Linq.Dynamic项目出现在Codeplex中。

这两个库都不是官方支持的LINQ提供程序。如果方便,您可以使用它们,但不应经常更新它们。

实际上,由于您似乎正在构建ASP.NET MVC应用程序,因此在视图或Javascript中对结果进行排序可能比在服务器端尝试这样做更好。大多数网格允许按列排序。

如果要对结果进行排序以进行分页,更好的选择是使用ORM的语言来实现,例如Entity SQL for Entity Framework或HQL for NHibernate。

答案 1 :(得分:2)

问题是动态库在我的项目中被引用了两次。至少我知道如果再次发生这样的事情需要注意什么。

答案 2 :(得分:1)

正如错误消息告诉您的那样,您正在将string传递给OrderBy,因为它期待Expression<Func<CompetencyAssessmentLineViewModel, TKey>>。你需要提供这样一个表达。

创建一个很难,因为您甚至不知道在编译时要排序的字段的类型。这意味着一旦为选择属性生成了正确的表达式,就必须使用反射来调用OrderBy,因为在编译时无法提供泛型参数。

private static Tuple<Expression, Type> GetSelector<T>(string propertyName)
{
    var parameter = Expression.Parameter(typeof(T));
    Expression body = Expression.Property(parameter, propertyName);

    return Tuple.Create(Expression.Lambda(body, parameter) as Expression
        , body.Type);
}

private static IOrderedQueryable<T> OrderBy<T>(IQueryable<T> query,
    string property, bool ascending)
{
    var selector = GetSelector<T>(property);
    Type[] argumentTypes = new[] { typeof(T), selector.Item2 };
    var methodName = ascending ? "OrderBy" : "OrderByDescending";

    var orderByMethod = typeof(Queryable).GetMethods()
        .First(method => method.Name == methodName
            && method.GetParameters().Count() == 2)
            .MakeGenericMethod(argumentTypes);
    return (IOrderedQueryable<T>)
        orderByMethod.Invoke(null, new object[] { query, selector.Item1 });
}

private static IOrderedQueryable<T> ThenBy<T>(IOrderedQueryable<T> query,
    string property, bool ascending)
{
    var selector = GetSelector<T>(property);
    Type[] argumentTypes = new[] { typeof(T), selector.Item2 };
    var methodName = ascending ? "ThenBy" : "ThenByDescending";

    var orderByMethod = typeof(Queryable).GetMethods()
        .First(method => method.Name == methodName
            && method.GetParameters().Count() == 2)
            .MakeGenericMethod(argumentTypes);
    return (IOrderedQueryable<T>)
        orderByMethod.Invoke(null, new object[] { query, selector.Item1 });
}

public static IOrderedQueryable<T> OrderBy<T>(
    this IQueryable<T> query,
    string property)
{
    return OrderBy<T>(query, property, true);
}
public static IOrderedQueryable<T> OrderByDescending<T>(
    this IQueryable<T> query,
    string property)
{
    return OrderBy<T>(query, property, false);
}
public static IOrderedQueryable<T> ThenBy<T>(
    this IOrderedQueryable<T> query,
    string property)
{
    return ThenBy<T>(query, property, true);
}
public static IOrderedQueryable<T> ThenByDescending<T>(
    this IOrderedQueryable<T> query,
    string property)
{
    return ThenBy<T>(query, property, false);
}

现在我们已经根据字符串属性完成了所有这些操作,您可以基本上完​​成以前想做的事情:

var displayList = list.OrderBy(request.SortingName)
    .ThenBy(request.SortingOrder)
    .Skip(startIndex)
    .Take(pageLength);