Динамический порядок LINQ от IEnumerable ‹T› / IQueryable ‹T›

Я нашел пример в примерах VS2008 для динамического LINQ, который позволяет использовать sql-подобная строка (например, OrderBy("Name, Age DESC")) для упорядочивания. К сожалению, включенный метод работает только на IQueryable<T>. Есть ли способ получить эту функциональность на IEnumerable<T>?


person John Sheehan    schedule 03.09.2008    source источник


Ответы (19)


Только что наткнулся на эту старичку ...

Чтобы сделать это без динамической библиотеки 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-to-Objects (деревья выражений для ORM и т. Д. Не могут действительно представлять dynamic запросы - MemberExpression не поддерживает его). Но вот способ сделать это с помощью 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);
        }
    }
}
person Marc Gravell    schedule 24.10.2008
comment
Лучший чертов код, который я видел :) Просто решил миллион проблем в моем проекте :) - person sajidnizami; 20.11.2008
comment
Марк, у вас есть подобное расширение для LIKE Condition? - person Prasad; 02.11.2009
comment
@Prasad - LIKE немного другое, но какой сервер? Для LINQ-to-SQL существует SqlMethods.Like: msdn.microsoft.com/en-us/library/; Вы можете добавить больше информации о том, что ищете? - person Marc Gravell; 02.11.2009
comment
Я использую SQL как бэкэнд, мне нужно выполнить операцию поиска с именами динамических параметров, как в вашем расширении OrderBy. - person Prasad; 02.11.2009
comment
Что ж, вы, безусловно, можете использовать SqlMethods.Like в пользовательском выражении; действительно непонятно где ломается ... можете уточнить? Может задать вопрос? - person Marc Gravell; 02.11.2009
comment
Спасибо, Марк, вчера я делал это в stackoverflow.com/questions/1654745/dynamic-linq-like. Лучше, если у меня будет образец - person Prasad; 02.11.2009
comment
Похоже, многие люди добились успеха с этим кодом - хоть убей, я не могу придумать, как его применить! Это методы расширения, верно? Как их использовать? - person Dave Swersky; 22.01.2010
comment
@Dave - вам нужно начать с IQueryable<T>, поэтому, если у вас есть что-то вроде List<T> (то есть IEnumerable<T>), вам может потребоваться использовать AsQueryable() - например var sorted = someList.AsQueryable().OrderBy("Foo.Bar"); - person Marc Gravell; 22.01.2010
comment
Вы видели это ... это может помочь некоторым людям ... stackoverflow.com/questions/557819/ это более строго типизированное решение. - person anthonyv; 08.05.2010
comment
Отличный код! Знаете ли вы, как добавить поддержку метода расширения Count ()? Вызов метода расширения - это тихая боль, и у меня проблемы с построением выражения с их помощью. Я пытаюсь добавить поддержку «Count ()» для IQueryable ‹T›, который закодирован как расширение в типе «Queryable». - person Mose; 17.05.2010
comment
@ile - var ordered = someData.OrderBy("Name"); - или для IEnumerable<T> данных var ordered = someData.AsQueryable().OrderBy("Name"); - person Marc Gravell; 03.06.2010
comment
Это можно сделать уже с помощью AsQueryAble (). OrderBy (). ThenBy (). Я что-то упускаю. Изменить. Я только что заметил, что вышеуказанные методы возвращают IOrderedQueryable, а перечисленные мной методы возвращают IOrderedEnumerable. - person Chuck Conway; 19.10.2010
comment
если использовать переключатель, который будет быстрее. отражение против переключателя - person Nario; 29.04.2011
comment
@ Нарио - контекст? включить отражение what или what? О, вы имеете в виду наращивание с .OrderBy(x => x.Something) (умножить на 10 или что-то еще) - скорее всего, switch - person Marc Gravell; 29.04.2011
comment
переключатель (имя свойства) {case property1: result = datacontex.orderby (x = ›x.property1) break; case property2: result = datacontex.orderby (x = ›x.property2) break; } - person Nario; 29.04.2011
comment
Спасибо за это! Я обнаружил одно улучшение: вместо оператора Reflection GetMethod () .. Invoke () вы можете построить результат непосредственно в linq: var expression = Expression.Call (typeof (Queryable), methodName, new [] {source. ElementType, expr.Type}, source.Expression, lambda); var result = source.Provider.CreateQuery ‹T› (выражение); - person automagic; 14.07.2011
comment
Можно ли использовать этот подход с объектом, реализующим IDynamicMetaObjectProvider? У меня возникли проблемы, описанные в этом сообщении: stackoverflow.com / questions / 11206631 / - person BonyT; 26.06.2012
comment
@BonyT нет, но я добавил динамический подход к вашему вопросу - person Marc Gravell; 26.06.2012
comment
@MarcGravell: я публикую вопрос по этому URL-адресу: stackoverflow.com / questions / 12495873 / dynamic-sort-in-linq /. и проблема в том, что когда я использую ваши методы расширения, результат не сортируется. в чем проблема? - person Arian; 22.09.2012
comment
Большое спасибо за этот код. Тот же вопрос, что и Моисей: знаете ли вы, как добавить поддержку метода расширения Count ()? - person Julien; 05.10.2012
comment
Есть ли способ применить сортировку по вложенной коллекции? - person amhed; 14.10.2012
comment
@ahmed зависит ... Что это означает? - person Marc Gravell; 14.10.2012
comment
Если кому-то интересно, я заставил его работать с IEnumerable, просто заменив lambda на lambda.Compile() в предпоследней строке, исключив очевидные замены IQueryableIEnumerable и IOrderedQueryable на IOrderedEnumerable. Обертки не задействованы - person Piddu; 22.05.2013
comment
Ничего себе, это смешно - пара строк кода увеличена до сорока, чтобы избежать использования System.Linq.Dynamic? Есть ли какое-то преимущество в том, чтобы не использовать его? Взгляните на ответ @Alaa Osta ниже (фактическое решение исходного вопроса). - person MGOwen; 13.07.2013
comment
@MGOwen, вы, кажется, неправильно понимаете природу кода. 40 строк одинаковы, независимо от того, 40 строк ли вы помещаете где-то в своем проекте, или эти строки поступают (предварительно скомпилированные или как исходные) во внешней библиотеке. Было бы довольно удивительно, если бы я связался в октябре 2008 года с библиотекой на nuget, которая существовала с декабря 2011 года (не в последнюю очередь потому, что nuget тогда тоже не существовал), но фундаментальный то, что он делает, то же самое. Кроме того, вы используете фразу «фактическое решение», как будто существует некий четко определенный согласованный единственный путь к каждому вопросу о кодировании: его нет. - person Marc Gravell; 13.07.2013
comment
@MGOwen, кстати, внешняя библиотека - это 2296 строк кода (не включая AssemblyInfo.cs); что делает 40 строк здесь довольно разумными - person Marc Gravell; 13.07.2013
comment
@MarcGravell Аххх Хорошо, извини, не заметил, что этот ответ был таким старым. Я был очень сбит с толку, как и 99% людей, которые зададут этот вопрос. Мой комментарий был адресован не вам, а им: сотням людей, которые задают этот вопрос и нуждаются в наилучшем доступном на данный момент ответе. WRT строки кода, я думаю, что большинство программистов предпочтут использовать установленную библиотеку для решения проблемы, и если я правильно понимаю связывание, в любом случае будет включен только используемый код, верно? - person MGOwen; 15.07.2013
comment
@MGOwen .net не использует компоновщик (за исключением некоторых фреймворков AOT). Будет включен весь IL, но только те методы, которые будут использоваться, будут редактироваться JIT. - person Marc Gravell; 15.07.2013
comment
Хорошо, только не переименовывайте никакие свойства. - person Adam Houldsworth; 01.10.2013
comment
@MarcGravell выполняет эту сортировку до или после запуска sql? Я также использую разбиение на страницы, чтобы получить только несколько записей за раз, но для получения правильных записей сортировка должна выполняться в операторе sql, а не в памяти. - person Zaphod; 13.01.2014
comment
@Zaphod, если ввод подходит IQueryable<T>, он включается в запрос без выполнения каких-либо действий в памяти. - person Marc Gravell; 13.01.2014
comment
Очень хороший код, он мне очень помог с сортировкой по имени столбца, хранящемуся в строке. Однако с интерфейсами это работает не очень хорошо. Я имею в виду, что если у вас есть IQueryable класса интерфейса и вы попытаетесь выполнить заказ по столбцу, который не является частью интерфейса, вы получите сообщение об ошибке. К счастью, это достаточно легко исправить. Замена всех экземпляров typeof (T) на source.ElementType позволит упорядочить по любому столбцу фактического класса, который обрабатывается в запросе. - person jahu; 30.01.2014
comment
@MarcGravell, в первом фрагменте вам не нужно вычислять самостоятельно delegateType LambdaExpression. Если вы не передаете его в качестве первого аргумента, он автоматически вычисляется Expression.Lambda. - person Francesco Abbruzzese; 29.12.2016
comment
Очень хорошо ... В моем очень простом тесте это выполняется за 29 мс против 148 мс System.Linq.Dynamic. - person Shawn Beachy; 27.07.2018
comment
Я не могу понять ни одной строчки из этого кода, но он работает! - person Jakub Szułakiewicz; 28.05.2019
comment
Я использую первую часть кода, и она работает очень здорово. Но после изменения проекта на .net Core 3.0 предупреждения, допускающие значение NULL, сводят меня с ума. Не могли бы вы обновить код? - person Henning; 14.10.2019
comment
@Henning (приставляет язык к щеке) - конечно! #nullable disable - person Marc Gravell; 14.10.2019
comment
@MarcGravell Привет, мастер, мне нужно избегать ввода, чтобы предотвратить инъекции? - person Nicholas; 02.12.2019
comment
@Nicholas no, слой парсера дерева выражений / генератора SQL имеет дело с этим, как с параметрами - person Marc Gravell; 02.12.2019

Слишком просто без каких-либо осложнений:

  1. Добавьте using System.Linq.Dynamic; вверху.
  2. Используйте vehicles = vehicles.AsQueryable().OrderBy("Make ASC, Year DESC").ToList();

Изменить: для экономии времени сборка System.Linq.Dynamic.Core (System.Linq.Dynamic устарела) не является частью платформы, но ее можно установить. из nuget: System.Linq.Dynamic.Core

person Alaa Osta    schedule 28.12.2011
comment
а откуда вы взяли System.Linq.Dynamic? - person Rafael Herscovici; 25.02.2013
comment
Работает также при использовании linq с MongoDB. - person soupy1976; 24.07.2013
comment
Принятый ответ, возможно, был правильным ответом в 2008 году, но в настоящее время это самый простой и самый правильный ответ. - person EL MOJO; 24.10.2014
comment
Это действительно хорошее и простое управление, такая большая внутренняя сложность, понравилось - person Mrinal Kamboj; 28.05.2016
comment
Можно ли сортировать по вложенному свойству?, Например: cars = cars.AsQueryable (). OrderBy (Status.Label ASC, Year DESC) .ToList (); - person Faly; 19.07.2017
comment
Я подумал, что принятый ответ был потрясающим, но это чистое золото! - person Ian; 22.09.2017
comment
В будущем, если вы используете ядро ​​dotnet, используйте это: nuget .org / packages / System.Linq.Dynamic.Core - person Rafael Merlin; 01.11.2017
comment
Для всех, кто просматривает это, как и я для того же решения - это работает для сортировки по вложенным объектам, то есть vehicles.AsQueryable().OrderBy("Tire.Size").ToList();, где Tire - это объект, принадлежащий Vehicle. - person neilsimp1; 11.01.2019
comment
@RafaelMerlin Также пространство имен теперь System.Linq.Dynamic.Core - person Edwin Stoteler; 09.06.2020
comment
nuget.org/packages/System.Linq.Dynamic.Core - это бесплатная или лицензионная версия? - person Raj; 28.07.2020

Просто наткнулся на этот вопрос.

Используя реализацию Марка ApplyOrder, описанную выше, я собрал метод Extension, который обрабатывает SQL-подобные строки, например:

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

Подробности можно найти здесь: http://aonnull.blogspot.com/2010/08/dynamic-sql-like-linq-orderby-extension.html.

person Adam Anderson    schedule 18.08.2010
comment
Отличный материал, просто добавьте следующую модификацию, чтобы сделать имя свойства нечувствительным к регистру: PropertyInfo pi = type.GetProperty (prop, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); - person Mrinal Kamboj; 29.05.2016

Я предполагаю, что было бы сработать использовать отражение, чтобы получить любое свойство, которое вы хотите отсортировать:

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

Обратите внимание, что использование отражения значительно медленнее, чем прямой доступ к свойству, поэтому производительность необходимо будет исследовать.

person Kjetil Watnedal    schedule 03.09.2008
comment
это вообще работает? orderby не требуется значение, а селектор lamba / delegate (Func ‹TSource, TKey› keySelector) .. - person Davy Landman; 24.10.2008
comment
Я попробовал этот пример перед тем, как опубликовать его, и да, он работает. - person Kjetil Watnedal; 28.10.2008
comment
+1 Это именно то, что я искал! Это отлично подойдет для простых задач сортировки страниц. - person Andrew Siemer; 19.04.2010
comment
У меня это не сработало. Я что-то упускаю? Каким должно быть SomeProperty. Я попытался указать имя свойства, а также property.GetType (). У меня IQueryable ‹›, а не IEnumerable ‹› - person SO User; 23.07.2010
comment
Метод GetPropertyValue будет выполняться для всех элементов, это плохое решение. - person Alex Shkor; 14.02.2012
comment
@Alex Shkor: Как вы должны сортировать элементы, не глядя на все элементы? Однако в других ответах есть лучшие решения. - person Kjetil Watnedal; 15.02.2012

Просто опираясь на то, что сказали другие. Я обнаружил, что следующее работает довольно хорошо.

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;
}
person vdhant    schedule 01.04.2009

Я наткнулся на этот вопрос, ища несколько предложений Linq orderby, и, возможно, это было то, что искал автор

Вот как это сделать:

var query = pets.OrderBy(pet => pet.Name).ThenByDescending(pet => pet.Age);    
person InfoStatus    schedule 16.12.2008
comment
+1 отменил голосование "против" из-за отсутствия объяснений. Я также думаю, что автора могли заинтересовать несколько заказов. Даже если "динамический" был ключевым словом, нет причин для отрицательного голосования. - person Jason Kleban; 25.05.2010

Я пытался это сделать, но у меня возникли проблемы с решением Кьетила Уотнедала, потому что я не использую встроенный синтаксис linq - я предпочитаю синтаксис в стиле метода. Моя конкретная проблема заключалась в попытке выполнить динамическую сортировку с использованием пользовательского 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, потому что я хотел сделать Естественная сортировка.

Для этого я просто изменяю OrderBy на:

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

Код см. В этом сообщении для NaturalSortComparer().

person James McCormack    schedule 29.10.2009

Использовать динамический linq

просто добавьте using System.Linq.Dynamic;

И используйте его так, чтобы упорядочить все свои столбцы:

string sortTypeStr = "ASC"; // or DESC
string SortColumnName = "Age"; // Your column name
query = query.OrderBy($"{SortColumnName} {sortTypeStr}");
person Masoud Darvishian    schedule 25.12.2018

После долгих поисков это сработало для меня:

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);
}
person Sanchitos    schedule 16.04.2013

Вы можете добавить это:

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 взята из ответа Кьетила Уотнедала

Вопрос в том, почему? Любая такая сортировка вызовет исключения во время выполнения, а не во время компиляции (например, ответ D2VIANT).

Если вы имеете дело с Linq to Sql и orderby является деревом выражений, он все равно будет преобразован в SQL для выполнения.

person Keith    schedule 03.09.2008
comment
Метод GetPropertyValue будет выполняться для всех элементов, это плохое решение. - person Alex Shkor; 14.02.2012
comment
OrderBy не сохраняйте прежний порядок !! - person Amir Ismail; 09.04.2012

Вот еще кое-что, что я нашел интересным. Если вашим источником является 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();
person Sameer Alibhai    schedule 13.01.2010

Благодаря Маартену (запрос к коллекции с помощью объекта PropertyInfo в 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)
    {}
}

OR

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

(убедитесь, что имена ваших столбцов соответствуют свойствам объекта)

Ваше здоровье

person joaopintocruz    schedule 16.10.2012

Вы можете преобразовать IEnumerable в IQueryable.

items = items.AsQueryable().OrderBy("Name ASC");
person Richard YS    schedule 30.07.2014

Сначала установите динамические Инструменты -> Диспетчер пакетов NuGet -> Консоль диспетчера пакетов

install-package System.Linq.Dynamic

Добавить пространство имен using System.Linq.Dynamic;

Теперь вы можете использовать OrderBy("Name, Age DESC")

person Aminur Rahman    schedule 10.03.2018
comment
Как я могу использовать его с внутренней сортировкой свойств - например, OrderBy (Branch.BranchName, Descending) - person devC; 23.05.2019
comment
У меня это работает. Возможно, потому, что этому вопросу 10 лет, а этот более простой метод появился позже. - person kosherjellyfish; 12.07.2019

Вы можете использовать это:

        public List<Book> Books(string orderField, bool desc, int skip, int take)
{
    var propertyInfo = typeof(Book).GetProperty(orderField);

    return _context.Books
        .Where(...)
        .OrderBy(p => !desc ? propertyInfo.GetValue(p, null) : 0)
        .ThenByDescending(p => desc ? propertyInfo.GetValue(p, null) : 0)
        .Skip(skip)
        .Take(take)
        .ToList();
}
person k1developer    schedule 16.03.2020
comment
Пару лет спустя я натыкаюсь на это; это сработало для меня, как во сне. У меня есть динамическая сортировка от 1 до 3 свойств, и это работает как мечта. Легко внедрить и без проблем. - person Bazïnga; 22.03.2020
comment
Мне нравится этот ответ, но как я могу заставить эту работу, если мне нужно отсортировать по свойству дочернего класса? - person RoLYroLLs; 21.07.2020

Альтернативное решение использует следующий класс / интерфейс. Это не совсем динамично, но работает.

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;
    }
}
person Mike Christiansen    schedule 16.03.2009

Этот ответ является ответом на комментарии, которые требуют примера решения, предоставленного @John Sheehan - Runscope

Приведите пример для остальных из нас.

в DAL (уровень доступа к данным),

Версия IEnumerable:

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

    return orders  // IEnumerable<Order>
}

Версия IQueryable

public IQueryable<Order> GetOrdersAsQuerable()
{
    IEnumerable<Order> qry= GetOrders();

    // use the built-in extension method  AsQueryable in  System.Linq namespace
    return qry.AsQueryable();            
}

Теперь вы можете использовать версию IQueryable для привязки, например, GridView в Asp.net и преимущества для сортировки (вы не можете сортировать с помощью версии IEnumerable)

Я использовал Dapper в качестве ORM и создал версию IQueryable, а также с легкостью использовал сортировку в GridView в asp.net.

person M.Hassan    schedule 23.11.2017

Преобразуйте List в IEnumerable или Iquerable, добавьте, используя пространство имен System.LINQ.Dynamic, затем вы можете указать имена свойств в строке, разделенной запятыми, в метод OrderBy, который по умолчанию поступает из System.LINQ.Dynamic.

person user145610    schedule 05.08.2013

вы можете сделать это для нескольких заказов

IOrderedEnumerable<JToken> sort;

if (query.OrderBys[0].IsDESC)
{
    sort = jarry.OrderByDescending(r => (string)r[query.OrderBys[0].Key]);
}
else
{
    sort = jarry.OrderBy(r =>
        (string) r[query.OrderBys[0].Key]); 
}

foreach (var item in query.OrderBys.Skip(1))
{
    if (item.IsDESC)
    {
        sort = sort.ThenByDescending(r => (string)r[item.Key]);
    }
    else
    {
        sort = sort.ThenBy(r => (string)r[item.Key]);
    }
}
person Francis Shaw    schedule 27.09.2020