关于c#:无法使动态OrderBy在我的通用列表上工作

Cannot get the dynamic OrderBy to work on my generic list

我不能让动态orderby处理我的常规列表;

1
2
3
4
5
6
7
8
9
10
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的动态功能。但是代码没有编译;

Error 1 The type arguments for method 'System.Linq.Queryable.OrderBy(System.Linq.IQueryable, System.Linq.Expressions.Expression>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

我做错什么了?

方法的签名是:

1
public JsonResult GridData(JqGridRequest request)

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

  • request.SortingName是一个带有字段名称的字符串,并且
  • request.SortingOrder是排序顺序

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


我怀疑你把IEnumerable和iqueryable与6年前ScottGuthrie提到的动态Linq库混淆了。这是必须添加到项目中的外部库。它的最新版本是2年前作为Nuget软件包发布的。

这个库有一些限制,所以去年codeplex中出现了另一个system.linq.dynamic项目。

这两个图书馆都不是官方支持的LINQ提供者。如果他们方便的话,你可以使用他们,但是你不应该经常更新他们。

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

如果您想为分页目的对结果进行排序,一个更好的选择是使用ORM的语言进行排序,例如Entity SQL for Entity Framework或HQL for NHibernate。


问题是动态库在我的项目中被引用了两次。至少我知道如果类似的事情再次发生,应该注意什么。


正如错误消息告诉您的那样,您正在将一个string传递给OrderBywen,它期望有一个Expression>。你需要提供这样的表达。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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);
}

既然我们已经准备好了基于字符串属性进行排序的所有这些内容,那么您基本上可以执行之前想要执行的操作:

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