首页 文章

IEnumerable <T> / IQueryable <T>上的动态LINQ OrderBy

提问于
浏览
630

我在动态LINQ的VS2008 Examples中找到了一个示例,它允许您使用类似sql的字符串(例如 OrderBy("Name, Age DESC")) 用于排序 . 不幸的是,包含的方法仅适用于 IQueryable<T> ; . 有什么方法可以在 IEnumerable<T> 上获得此功能?

18 回答

  • 4

    这是我觉得有趣的其他东西 . 如果您的源是DataTable,则可以在不使用Dynamic Linq的情况下使用动态排序

    DataTable orders = dataSet.Tables["SalesOrderHeader"];
    EnumerableRowCollection<DataRow> query = from order in orders.AsEnumerable()
                                             orderby order.Field<DateTime>("OrderDate")
                                             select order;
    DataView view = query.AsDataView();
    bindingSource1.DataSource = view;
    

    参考:http://msdn.microsoft.com/en-us/library/bb669083.aspx(使用DataSetExtensions)

    通过将其转换为DataView,还有另一种方法:

    DataTable contacts = dataSet.Tables["Contact"];    
    DataView view = contacts.AsDataView();    
    view.Sort = "LastName desc, FirstName asc";    
    bindingSource1.DataSource = view;
    dataGridView1.AutoResizeColumns();
    
  • 4

    我绊倒了这个问题,寻找Linq多个orderby子句,也许这就是作者所寻求的

    以下是如何做到这一点:

    var query = pets.OrderBy(pet => pet.Name).ThenByDescending(pet => pet.Age);
    
  • 9

    偶然发现了这个问题 .

    使用上面的Marc的ApplyOrder实现,我把一个处理类似SQL的字符串的Extension方法拼凑在一起,如:

    list.OrderBy("MyProperty DESC, MyOtherProperty ASC");
    

    详情请见:http://aonnull.blogspot.com/2010/08/dynamic-sql-like-linq-orderby-extension.html

  • 0

    首先安装动态 Tools --> NuGet Package Manager --> Package Manager Console

    install-package System.Linq.Dynamic
    

    添加 Namespace using System.Linq.Dynamic;

    现在你可以使用 OrderBy("Name, Age DESC")

  • 4

    感谢Maarten(Query a collection using PropertyInfo object in LINQ)我得到了这个解决方案:

    myList.OrderByDescending(x => myPropertyInfo.GetValue(x, null)).ToList();
    

    在我的情况下,我正在开发一个“ColumnHeaderMouseClick”(WindowsForm),所以只是发现按下的特定列及其对应的PropertyInfo:

    foreach (PropertyInfo column in (new Process()).GetType().GetProperties())
    {
        if (column.Name == dgvProcessList.Columns[e.ColumnIndex].Name)
        {}
    }
    

    要么

    PropertyInfo column = (new Process()).GetType().GetProperties().Where(x => x.Name == dgvProcessList.Columns[e.ColumnIndex].Name).First();
    

    (确保您的列名称与对象属性匹配)

    干杯

  • 77

    我想使用反射来获取你想要排序的任何属性是有用的:

    IEnumerable<T> myEnumerables
    var query=from enumerable in myenumerables
              where some criteria
              orderby GetPropertyValue(enumerable,"SomeProperty")
              select enumerable
    
    private static object GetPropertyValue(object obj, string property)
    {
        System.Reflection.PropertyInfo propertyInfo=obj.GetType().GetProperty(property);
        return propertyInfo.GetValue(obj, null);
    }
    

    请注意,使用反射比直接访问属性要慢得多,因此必须调查性能 .

  • 211

    你可以添加它:

    public static IEnumerable<T> OrderBy( this IEnumerable<T> input, string queryString) {
        //parse the string into property names
        //Use reflection to get and sort by properties
        //something like
    
        foreach( string propname in queryString.Split(','))
            input.OrderBy( x => GetPropertyValue( x, propname ) );
    
        // I used Kjetil Watnedal's reflection example
    }
    

    GetPropertyValue 函数来自Kjetil Watnedal's answer

    问题是为什么?任何这样的排序都会在运行时抛出异常,而不是编译时间(如D2VIANT的答案) .

    如果您正在处理Linq到Sql并且orderby是一个表达式树,它将被转换为SQL以供执行 .

  • 18

    我试图这样做但是因为我没有使用内联linq语法而遇到Kjetil Watnedal's solution问题 - 我更喜欢方法式语法 . 我的具体问题是尝试使用自定义 IComparer 进行动态排序 .

    我的解决方案最终如下:

    给出一个IQueryable查询,如下所示:

    List<DATA__Security__Team> teams = TeamManager.GetTeams();
    var query = teams.Where(team => team.ID < 10).AsQueryable();
    

    并给出了运行时排序字段参数:

    string SortField; // Set at run-time to "Name"
    

    动态OrderBy看起来像这样:

    query = query.OrderBy(item => item.GetReflectedPropertyValue(SortField));
    

    这是使用一个名为GetReflectedPropertyValue()的辅助方法:

    public static string GetReflectedPropertyValue(this object subject, string field)
    {
        object reflectedValue = subject.GetType().GetProperty(field).GetValue(subject, null);
        return reflectedValue != null ? reflectedValue.ToString() : "";
    }
    

    最后一件事 - 我提到我希望 OrderBy 使用自定义 IComparer - 因为我想做Natural sorting .

    要做到这一点,我只需将 OrderBy 改为:

    query = query.OrderBy(item => item.GetReflectedPropertyValue(SortField), new NaturalSortComparer<string>());
    

    有关 NaturalSortComparer() 的代码,请参阅this post .

  • 4

    这个答案是对需要@John Sheehan - Runscope提供的解决方案示例的评论的回应

    请为我们其他人提供一个例子 .

    在DAL(数据访问层)中,

    The IEnumerable version:

    public  IEnumerable<Order> GetOrders()
        {
          // i use Dapper to return IEnumerable<T> using Query<T>
          //.. do stuff
          return  orders  // IEnumerable<Order>
      }
    

    The IQueryable version

    public IQueryable<Order> GetOrdersAsQuerable()
        {
            IEnumerable<Order> qry= GetOrders();
            //use the built-in extension method  AsQueryable in  System.Linq namespace
            return qry.AsQueryable();            
        }
    

    现在您可以使用IQueryable版本进行绑定,例如Asp.net中的GridView并有利于排序(您无法使用IEnumerable版本进行排序)

    我使用Dapper作为ORM并构建IQueryable版本并在asp.net中使用GridView中的排序非常简单 .

  • 2

    经过大量的搜索,这对我有用:

    public static IEnumerable<TEntity> OrderBy<TEntity>(this IEnumerable<TEntity> source, 
                                                        string orderByProperty, bool desc)
    {
        string command = desc ? "OrderByDescending" : "OrderBy";
        var type = typeof(TEntity);
        var property = type.GetProperty(orderByProperty);
        var parameter = Expression.Parameter(type, "p");
        var propertyAccess = Expression.MakeMemberAccess(parameter, property);
        var orderByExpression = Expression.Lambda(propertyAccess, parameter);
        var resultExpression = Expression.Call(typeof(Queryable), command, 
                                               new[] { type, property.PropertyType },
                                               source.AsQueryable().Expression, 
                                               Expression.Quote(orderByExpression));
        return source.AsQueryable().Provider.CreateQuery<TEntity>(resultExpression);
    }
    
  • 50

    您可以将IEnumerable转换为IQueryable .

    items = items.AsQueryable().OrderBy("Name ASC");
    
  • 1

    将List转换为IEnumerable或Iquerable,使用System.LINQ.Dynamic命名空间添加,然后你可以用逗号分隔的字符串中的属性名称提到OrderBy方法,该方法默认来自System.LINQ.Dynamic .

  • 39

    备用解决方案使用以下类/接口 . 它不是真正的动态,但它的工作原理 .

    public interface IID
    {
        int ID
        {
            get; set;
        }
    }
    
    public static class Utils
    {
        public static int GetID<T>(ObjectQuery<T> items) where T:EntityObject, IID
        {
            if (items.Count() == 0) return 1;
            return items.OrderByDescending(u => u.ID).FirstOrDefault().ID + 1;
        }
    }
    
  • 1
    var result1 = lst.OrderBy(a=>a.Name);// for ascending order. 
     var result1 = lst.OrderByDescending(a=>a.Name);// for desc order.
    
  • -2

    没有任何复杂情况太容易了:

    • 在顶部添加 using System.Linq.Dynamic; .

    • 使用 vehicles = vehicles.AsQueryable().OrderBy("Make ASC, Year DESC").ToList();

  • 4

    只是 Build 在别人所说的基础之上 . 我发现以下效果很好 .

    public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> input, string queryString)
    {
        if (string.IsNullOrEmpty(queryString))
            return input;
    
        int i = 0;
        foreach (string propname in queryString.Split(','))
        {
            var subContent = propname.Split('|');
            if (Convert.ToInt32(subContent[1].Trim()) == 0)
            {
                if (i == 0)
                    input = input.OrderBy(x => GetPropertyValue(x, subContent[0].Trim()));
                else
                    input = ((IOrderedEnumerable<T>)input).ThenBy(x => GetPropertyValue(x, subContent[0].Trim()));
            }
            else
            {
                if (i == 0)
                    input = input.OrderByDescending(x => GetPropertyValue(x, subContent[0].Trim()));
                else
                    input = ((IOrderedEnumerable<T>)input).ThenByDescending(x => GetPropertyValue(x, subContent[0].Trim()));
            }
            i++;
        }
    
        return input;
    }
    
  • 860

    我找到了答案 . 我可以使用 .AsQueryable<>() 扩展方法将我的列表转换为IQueryable,然后通过它来运行动态订单 .

  • 11

    只是偶然发现这个老人......

    要在没有动态LINQ库的情况下执行此操作,您只需要以下代码即可 . 这涵盖了大多数常见方案,包括嵌套属性 .

    要使用 IEnumerable<T> ,您可以添加一些通过 AsQueryable 的包装器方法 - 但下面的代码是所需的核心 Expression 逻辑 .

    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 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;
    }
    

    编辑:如果你想将它与 dynamic 混合,它会变得更有趣 - 尽管注意 dynamic 仅适用于LINQ到对象(ORM等的表达式树不能真正代表 dynamic 查询 - MemberExpression 不是't support it). But here'这样做的方法使用LINQ-to-Objects . 注意 Hashtable 的选择是由于有利的锁定语义:

    using Microsoft.CSharp.RuntimeBinder;
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Dynamic;
    using System.Linq;
    using System.Runtime.CompilerServices;
    static class Program
    {
        private static class AccessorCache
        {
            private static readonly Hashtable accessors = new Hashtable();
    
            private static readonly Hashtable callSites = new Hashtable();
    
            private static CallSite<Func<CallSite, object, object>> GetCallSiteLocked(
                string name) 
            {
                var callSite = (CallSite<Func<CallSite, object, object>>)callSites[name];
                if(callSite == null)
                {
                    callSites[name] = callSite = CallSite<Func<CallSite, object, object>>
                        .Create(Binder.GetMember(
                                    CSharpBinderFlags.None, 
                                    name, 
                                    typeof(AccessorCache),
                                    new CSharpArgumentInfo[] { 
                                        CSharpArgumentInfo.Create(
                                            CSharpArgumentInfoFlags.None, 
                                            null) 
                                    }));
                }
                return callSite;
            }
    
            internal static Func<dynamic,object> GetAccessor(string name)
            {
                Func<dynamic, object> accessor = (Func<dynamic, object>)accessors[name];
                if (accessor == null)
                {
                    lock (accessors )
                    {
                        accessor = (Func<dynamic, object>)accessors[name];
                        if (accessor == null)
                        {
                            if(name.IndexOf('.') >= 0) {
                                string[] props = name.Split('.');
                                CallSite<Func<CallSite, object, object>>[] arr 
                                    = Array.ConvertAll(props, GetCallSiteLocked);
                                accessor = target =>
                                {
                                    object val = (object)target;
                                    for (int i = 0; i < arr.Length; i++)
                                    {
                                        var cs = arr[i];
                                        val = cs.Target(cs, val);
                                    }
                                    return val;
                                };
                            } else {
                                var callSite = GetCallSiteLocked(name);
                                accessor = target =>
                                {
                                    return callSite.Target(callSite, (object)target);
                                };
                            }
                            accessors[name] = accessor;
                        }
                    }
                }
                return accessor;
            }
        }
    
        public static IOrderedEnumerable<dynamic> OrderBy(
            this IEnumerable<dynamic> source, 
            string property)
        {
            return Enumerable.OrderBy<dynamic, object>(
                source, 
                AccessorCache.GetAccessor(property), 
                Comparer<object>.Default);
        }
    
        public static IOrderedEnumerable<dynamic> OrderByDescending(
            this IEnumerable<dynamic> source, 
            string property)
        {
            return Enumerable.OrderByDescending<dynamic, object>(
                source, 
                AccessorCache.GetAccessor(property), 
                Comparer<object>.Default);
        }
    
        public static IOrderedEnumerable<dynamic> ThenBy(
            this IOrderedEnumerable<dynamic> source, 
            string property)
        {
            return Enumerable.ThenBy<dynamic, object>(
                source, 
                AccessorCache.GetAccessor(property), 
                Comparer<object>.Default);
        }
    
        public static IOrderedEnumerable<dynamic> ThenByDescending(
            this IOrderedEnumerable<dynamic> source, 
            string property)
        {
            return Enumerable.ThenByDescending<dynamic, object>(
                source, 
                AccessorCache.GetAccessor(property), 
                Comparer<object>.Default);
        }
    
        static void Main()
        {
            dynamic a = new ExpandoObject(), 
                    b = new ExpandoObject(), 
                    c = new ExpandoObject();
            a.X = "abc";
            b.X = "ghi";
            c.X = "def";
            dynamic[] data = new[] { 
                new { Y = a },
                new { Y = b }, 
                new { Y = c } 
            };
    
            var ordered = data.OrderByDescending("Y.X").ToArray();
            foreach (var obj in ordered)
            {
                Console.WriteLine(obj.Y.X);
            }
        }
    }
    

相关问题