【问题标题】:Dynamically Sorting with LINQ使用 LINQ 动态排序
【发布时间】:2011-01-18 15:50:11
【问题描述】:

我有一个 CLR 对象的集合。对象的类定义具有三个属性:FirstName、LastName、BirthDate。

我有一个字符串,它反映了集合应该作为排序依据的属性的名称。另外,我有一个排序方向。如何将此排序信息动态应用于我的收藏?请注意,排序可以是多层的,例如我可以先按姓氏排序,然后再按名字排序。

目前,我正在尝试以下方法,但没有任何运气:

var results = myCollection.OrderBy(sortProperty);

但是,我收到一条消息:

...不包含“OrderBy”的定义,并且最好的扩展方法重载...有一些无效的参数。

【问题讨论】:

    标签: linq


    【解决方案1】:

    好的,我在他的 cmets 中与 SLaks 的争论迫使我想出了一个答案 :)

    我假设您只需要支持 LINQ to Objects。下面是一些需要添加大量验证但确实有效的代码:

    // We want the overload which doesn't take an EqualityComparer.
    private static MethodInfo OrderByMethod = typeof(Enumerable)
        .GetMethods(BindingFlags.Public | BindingFlags.Static)
        .Where(method => method.Name == "OrderBy" 
               && method.GetParameters().Length == 2)
        .Single();
    
    public static IOrderedEnumerable<TSource> OrderByProperty<TSource>(
        this IEnumerable<TSource> source,
        string propertyName) 
    {
        // TODO: Lots of validation :)
        PropertyInfo property = typeof(TSource).GetProperty(propertyName);
        MethodInfo getter = property.GetGetMethod();
        Type propType = property.PropertyType;
        Type funcType = typeof(Func<,>).MakeGenericType(typeof(TSource), propType);
        Delegate func = Delegate.CreateDelegate(funcType, getter);
        MethodInfo constructedMethod = OrderByMethod.MakeGenericMethod(
            typeof(TSource), propType);
        return (IOrderedEnumerable<TSource>) constructedMethod.Invoke(null,
            new object[] { source, func });
    }
    

    测试代码:

    string[] foo = new string[] { "Jon", "Holly", "Tom", "William", "Robin" };
    
    foreach (string x in foo.OrderByProperty("Length"))
    {
        Console.WriteLine(x);
    }
    

    输出:

    Jon
    Tom
    Holly
    Robin
    William
    

    它甚至返回一个IOrderedEnumerable&lt;TSource&gt;,所以你可以像往常一样链接ThenBy子句:)

    【讨论】:

    • customers.OrderByProperty("LastName").ThenByProperty("FirstName")customers.OrderByProperty("LastName", "FirstName")(这些是 params string[])怎么样,因为这似乎是他的要求的一部分。这在这个解决方案中可行吗?
    • @Anthony:当然,您必须编写更多代码才能做到这一点,但这是可能的——并且看起来很像上面的代码。
    • 好的。由于我是一个知道jack squat 关于这个话题的人,你是否有理由更喜欢反射而不是@SLaks 使用表达式树? (以防我将来遇到这样的要求。)
    • @Anthony:嗯,我的版本直接与 LINQ to Objects 一起使用 - SLaks 的版本需要 IQueryable&lt;T&gt;。您可以使用 AsQueryable 运算符创建一个,但我个人会坚持使用此版本。
    • @Jon, @Anthony:我的版本不需要IQueryable&lt;T&gt;;你可以编译表达式。但是,它会比你的慢,因为它需要编译一个表达式。 (可以缓存)
    【解决方案2】:

    您需要构建一个Expression Tree 并将其传递给OrderBy
    它看起来像这样:

    var param = Expression.Parameter(typeof(MyClass));
    var expression = Expression.Lambda<Func<MyClass, PropertyType>>(
        Expression.Property(param, sortProperty),
        param
    );
    

    或者,您可以使用Dynamic LINQ,这将使您的代码按原样工作。

    【讨论】:

    • 如何构建表达式树?
    • 鉴于 OP 有“CLR 对象的集合”,我怀疑他不是在使用 IQueryable 解决方案。
    • @Jon:那又怎样?您仍然需要一个表达式树来动态排序。这只是意味着他需要致电Compile
    • @SLaks:我不同意。我认为完全可以在没有表达式树的情况下进行动态排序。
    • 如果 sortProperty 的类型不固定,这个解决方案(或 Jon 的假设解决方案)的适用性如何?
    【解决方案3】:
    protected void sort_grd(object sender, GridViewSortEventArgs e)
        {
            if (Convert.ToBoolean(ViewState["order"]) == true)
            {
                ViewState["order"] = false;
    
            }
            else
            {
                ViewState["order"] = true;
            }
            ViewState["SortExp"] = e.SortExpression;
            dataBind(Convert.ToBoolean(ViewState["order"]), e.SortExpression);
        }
    
    public void dataBind(bool ord, string SortExp)
        {
            var db = new DataClasses1DataContext(); //linq to sql class
            var Name = from Ban in db.tbl_Names.AsEnumerable()
                             select new
                             {
                                 First_Name = Ban.Banner_Name,
                                 Last_Name = Ban.Banner_Project
                             };
            if (ord)
            {
                Name = BannerName.OrderBy(q => q.GetType().GetProperty(SortExp).GetValue(q, null));
            }
            else
            {
                Name = BannerName.OrderByDescending(q => q.GetType().GetProperty(SortExp).GetValue(q, null));
            }
            grdSelectColumn.DataSource = Name ;
            grdSelectColumn.DataBind();
        }
    

    【讨论】:

      【解决方案4】:

      你可以用 Linq 做到这一点

      var results = from c in myCollection
          orderby c.SortProperty
          select c;
      

      【讨论】:

      • 问题是,我的 sortProperty 是一个字符串。我怎样才能对这样的字符串进行排序?
      • 啊,我明白了。在这里找到另一个可能有帮助的帖子...stackoverflow.com/questions/4275878/…
      【解决方案5】:

      对于动态排序,您可以评估字符串,例如

      List<MyObject> foo = new List<MyObject>();
      string sortProperty = "LastName";
      var result = foo.OrderBy(x =>
                      {
                      if (sortProperty == "LastName")
                          return x.LastName;
                      else
                          return x.FirstName;
                      });
      

      有关更通用的解决方案,请参阅此 SO 线程:Strongly typed dynamic Linq sorting

      【讨论】:

      • 同意,这不是一个非常通用的解决方案,但解决了这个特殊问题
      【解决方案6】:

      【讨论】:

      • 我误会了,我已经更新了我的答案,建议使用 Dynamic Linq 库。
      【解决方案7】:

      您可以复制粘贴我在该答案中发布的方法,并更改签名/方法名称: How to make the position of a LINQ Query SELECT variable

      【讨论】:

        【解决方案8】:

        你实际上可以使用你原来的代码行

        var results = myCollection.OrderBy(sortProperty);
        

        只需使用System.Linq.Dynamic 库。

        如果您遇到编译器错误(例如无法转换或不包含定义...),您可能必须这样做:

        var results = myCollection.AsQueryable().OrderBy(sortProperty);
        

        不需要任何表达式树或数据绑定。

        【讨论】:

          【解决方案9】:

          您将需要使用反射来获取 PropertyInfo,然后使用它来构建表达式树。像这样的:

          var entityType = typeof(TEntity);
          var prop = entityType.GetProperty(sortProperty);
          var param = Expression.Parameter(entityType, "x");
          var access = Expression.Lambda(Expression.MakeMemberAccess(param, prop), param);
          
          var ordered = (IOrderedQueryable<TEntity>) Queryable.OrderBy(
              myCollection, 
              (dynamic) access);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-10-08
            • 2015-10-17
            • 2010-12-31
            • 1970-01-01
            相关资源
            最近更新 更多