Динамический LINQ OrderBy в IEnumerable <T> / IQueryable <T>

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

На мой взгляд, лучший ответ на эту дату: библиотека System.Linq.Dynamic.Core.

Shahin Dohan 08.10.2018 01:50
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
690
1
281 731
20
Перейти к ответу Данный вопрос помечен как решенный

Ответы 20

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

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

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

это вообще работает? orderby не требуется значение, а селектор lamba / delegate (Func <TSource, TKey> keySelector) ..

Davy Landman 24.10.2008 16:58

Я попробовал этот пример перед тем, как опубликовать его, и да, он работает.

Kjetil Watnedal 28.10.2008 10:22

+1 Это именно то, что я искал! Это отлично подходит для простых задач сортировки страниц.

Andrew Siemer 19.04.2010 08:08

У меня это не сработало. Я что-то пропустил? Какой должна быть SomeProperty. Я попытался указать имя свойства, а также property.GetType (). У меня IQueryable <>, а не IEnumerable <>

SO User 23.07.2010 09:59

Метод GetPropertyValue будет выполняться для всех элементов, это плохое решение.

Alex Shkor 14.02.2012 20:25

@Alex Shkor: Как вы должны сортировать элементы, не глядя на все элементы? Однако в других ответах есть лучшие решения.

Kjetil Watnedal 15.02.2012 16:58

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

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 для выполнения.

Метод GetPropertyValue будет выполняться для всех элементов, это плохое решение.

Alex Shkor 14.02.2012 20:27

OrderBy не поддерживает предыдущий порядок !!

Amir Ismail 09.04.2012 13:13
Ответ принят как подходящий

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

Чтобы сделать это без динамической библиотеки 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);
        }
    }
}

Лучший чертов код, который я видел :) Просто решил миллион проблем в моем проекте :)

sajidnizami 20.11.2008 12:37

Марк, у вас есть подобное расширение для LIKE Condition?

Prasad 02.11.2009 06:30

@Prasad - LIKE немного отличается, но какой сервер? Для LINQ-to-SQL есть SqlMethods.Like: msdn.microsoft.com/en-us/library/…; Вы можете добавить больше информации о том, что ищете?

Marc Gravell 02.11.2009 08:20

Я использую SQL как бэкэнд, мне нужно выполнить операцию поиска с именами динамических параметров, как в вашем расширении OrderBy.

Prasad 02.11.2009 08:55

Что ж, вы, безусловно, можете использовать SqlMethods.Like в собственном выражении; действительно непонятно где ломается ... можете уточнить? Может задать вопрос?

Marc Gravell 02.11.2009 09:16

Спасибо, Марк, вчера делал в stackoverflow.com/questions/1654745/dynamic-linq-like. Лучше, если у меня будет образец

Prasad 02.11.2009 09:30

Похоже, многие люди добились успеха с этим кодом - хоть убей, я не могу придумать, как его применить! Это методы расширения, верно? Как их использовать ??

Dave Swersky 22.01.2010 21:22

@Dave - вам нужно начать с IQueryable<T>, поэтому, если у вас есть что-то вроде List<T> (то есть IEnumerable<T>), вам может потребоваться использовать AsQueryable() - например, var sorted = someList.AsQueryable().OrderBy("Foo.Bar");

Marc Gravell 22.01.2010 22:26

Вы видели это ... это может помочь некоторым людям ... stackoverflow.com/questions/557819/… это более строго типизированное решение.

anthonyv 08.05.2010 16:30

Отличный код! Знаете ли вы, как добавить поддержку метода расширения «Count ()»? Вызов метода расширения - это тихая боль, и у меня проблемы с построением выражения с их помощью. Я пытаюсь добавить поддержку «Count ()» для IQueryable <T>, которая кодируется как расширение в типе «Queryable».

Mose 17.05.2010 16:24

@ile - var ordered = someData.OrderBy("Name"); - или для данных IEnumerable<T>, var ordered = someData.AsQueryable().OrderBy("Name");

Marc Gravell 03.06.2010 14:01

Это можно сделать уже с AsQueryAble (). OrderBy (). ThenBy (). Я что-то упускаю. Редактировать Я только что заметил, что вышеуказанные методы возвращают IOrderedQueryable, а перечисленные мной методы возвращают IOrderedEnumerable.

Chuck Conway 19.10.2010 01:49

если использовать переключатель, который будет быстрее. отражение против переключателя

Nario 29.04.2011 12:41

@ Нарио - контекст? включить отражение Какие, vs Какие? О, вы имеете в виду наращивание с помощью .OrderBy(x => x.Something) (умноженное на 10 или что-то еще) - скорее всего, switch

Marc Gravell 29.04.2011 13:36

переключатель (название свойства) {case "property1": result = datacontex.orderby (x => x.property1) break; case "property2": result = datacontex.orderby (x => x.property2) break; }

Nario 29.04.2011 14:18

Спасибо за это! Я обнаружил одно улучшение: вместо оператора 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> (выражение);

automagic 14.07.2011 01:14

Можно ли использовать этот подход с объектом, реализующим IDynamicMetaObjectProvider? У меня проблемы, описанные в этом посте: stackoverflow.com/questions/11206631/…

BonyT 26.06.2012 15:50

@BonyT нет, но я добавил "динамический" подход к вашему вопросу

Marc Gravell 26.06.2012 16:20

@MarcGravell: Я публикую вопрос по этому адресу: stackoverflow.com/questions/12495873/dynamic-sort-in-linq/…. и проблема в том, что когда я использую ваши методы расширения, результат не сортируется. в чем проблема?

Arian 22.09.2012 16:20

Большое спасибо за этот код. Тот же вопрос, что и Моисей: знаете ли вы, как добавить поддержку метода расширения «Count ()»?

Julien 05.10.2012 18:42

Есть ли способ применить сортировку по вложенной коллекции?

amhed 14.10.2012 17:31

@ahmed зависит ... Что это за иметь в виду?

Marc Gravell 14.10.2012 23:25

Если кому-то интересно, я заставил его работать с IEnumerable, просто заменив lambda на lambda.Compile() в предпоследней строке, исключив очевидные замены IQueryable> IEnumerable и IOrderedQueryable на IOrderedEnumerable. Обертки не задействованы

Piddu 22.05.2013 11:11

Вау, это смешно - пара строк кода увеличена до сорока, чтобы избежать использования System.Linq.Dynamic? Есть ли какое-то преимущество в том, чтобы не использовать его? Взгляните на ответ @Alaa Osta ниже (собственно решение исходного вопроса).

MGOwen 13.07.2013 05:11

@MGOwen, вы, кажется, неправильно понимаете природу кода. 40 строк одинаковы, независимо от того, 40 строк ли вы помещаете где-то в своем проекте, или эти строки поступают (предварительно скомпилированные или как исходные) во внешней библиотеке. Это было бы довольно удивительно, если бы я связался в октябре 2008 года с библиотекой на nuget, которая существовала с декабря 2011 года (не в последнюю очередь потому, что nuget тогда тоже не существовал), но фундаментальным «что он делает» является одно и тоже. Кроме того, вы используете фразу «фактическое решение», как будто существует некий четко определенный согласованный единственный путь к каждому вопросу кодирования: его нет.

Marc Gravell 13.07.2013 12:15

@MGOwen, кстати, внешняя библиотека - это 2296 строк кода (не включая AssemblyInfo.cs); из-за чего 40 строк здесь выглядят довольно разумно

Marc Gravell 13.07.2013 12:18

@MarcGravell Аххх Хорошо, извини, не заметил, что этот ответ был таким старым. Я был очень сбит с толку, как и 99% людей, которые зададут этот вопрос. Мой комментарий был адресован не вам, а им: сотням людей, которые задают этот вопрос и нуждаются в наилучшем доступном на данный момент ответе. WRT строки кода, я думаю, что большинство программистов предпочтут использовать установленную библиотеку для решения проблемы, и если я правильно понимаю связывание, в любом случае будет включен только используемый код, верно?

MGOwen 15.07.2013 06:03

@MGOwen .net не использует компоновщик (за исключением некоторых фреймворков AOT). Будет включен весь IL, но только те методы, которые используются, будут JIT-ed

Marc Gravell 15.07.2013 10:38

Хорошо, только не переименовывайте никакие свойства.

Adam Houldsworth 01.10.2013 14:15

@MarcGravell выполняет эту сортировку до или после запуска sql? Я также использую разбиение на страницы, чтобы получить только несколько записей за раз, но для получения правильных записей сортировка должна выполняться в операторе sql, а не в памяти.

Zaphod 13.01.2014 18:20

@Zaphod до тех пор, пока входной сигнал является подходящим IQueryable<T>, он включается в запрос без выполнения каких-либо действий в памяти.

Marc Gravell 13.01.2014 19:38

Очень хороший код, он мне очень помог с сортировкой по имени столбца, хранящемуся в строке. Однако с интерфейсами это работает не очень хорошо. Я имею в виду, что если у вас есть IQueryable класса интерфейса и вы попытаетесь выполнить заказ по столбцу, который не является частью интерфейса, вы получите сообщение об ошибке. К счастью, это достаточно легко исправить. Замена всех экземпляров typeof (T) на source.ElementType позволит упорядочить по любому столбцу фактического класса, который обрабатывается в запросе.

jahu 30.01.2014 20:30

@MarcGravell, в первом фрагменте вам не нужно вычислять самостоятельно delegateType LambdaExpression. Если вы не передаете его в качестве первого аргумента, он автоматически вычисляется Expression.Lambda.

Francesco Abbruzzese 29.12.2016 22:17

Очень хорошо ... В моем очень простом тесте это выполняется за 29 мс против 148 мс System.Linq.Dynamic.

Tastes Like Turkey 27.07.2018 20:27

Я не могу понять ни одной строчки из этого кода, но он работает!

Jakub Szułakiewicz 28.05.2019 10:32

Я использую первую часть кода, и она работает очень здорово. Но после изменения проекта на .net Core 3.0 предупреждения, допускающие значение NULL, сводят меня с ума. Не могли бы вы обновить код?

Henning 14.10.2019 11:35

@Henning (приставляет язык к щеке) - конечно! #nullable disable

Marc Gravell 14.10.2019 12:09

@MarcGravell Привет, мастер, мне нужно избегать ввода, чтобы предотвратить инъекции?

Nicholas 02.12.2019 18:55

@Nicholas no, слой парсера дерева выражений / генератора SQL имеет дело с этим, как с параметрами

Marc Gravell 02.12.2019 19:18

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

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

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

+1 отменил голосование против из-за отсутствия объяснений. Я также думаю, что автора могли заинтересовать несколько заказов. Даже если динамическое был ключевое слово, нет причин голосовать против.

Jason Kleban 25.05.2010 22:51

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

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

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

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

Я пытался сделать это, но у меня возникли проблемы с Решение Кьетила Ватнедала, потому что я не использую встроенный синтаксис 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() в эта почта.

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

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

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

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

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

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

Mrinal Kamboj 29.05.2016 10:47

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

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

а где ты System.Linq.Dynamic взял?

Rafael Herscovici 25.02.2013 18:12

Работает также при использовании linq с MongoDB.

soupy1976 24.07.2013 14:02

Принятый ответ, возможно, был правильным ответом в 2008 году, но в настоящее время это самый простой и самый правильный ответ.

EL MOJO 24.10.2014 19:33

Это действительно хорошее и простое управление, такая большая внутренняя сложность, понравилось

Mrinal Kamboj 28.05.2016 17:25

Можно ли сортировать по вложенному свойству?, Например: cars = cars.AsQueryable (). OrderBy ("Status.Label ASC, Year DESC"). ToList ();

Faly 19.07.2017 12:35

Я подумал, что принятый ответ был потрясающим, но это чистое золото!

Ian 22.09.2017 14:09

Для людей в «будущем», если вы используете ядро ​​dotnet, используйте это: nuget.org/packages/System.Linq.Dynamic.Core

Rafael Merlin 01.11.2017 05:08

Для всех, кто просматривает это, как и я для того же решения - это работает для сортировки «вложенных» объектов, то есть vehicles.AsQueryable().OrderBy("Tire.Size").ToList();, где Tire - это объект, принадлежащий Vehicle.

neilsimp1 11.01.2019 16:42

@RafaelMerlin Также пространство имен теперь System.Linq.Dynamic.Core

Edwin Stoteler 09.06.2020 13:53

nuget.org/packages/System.Linq.Dynamic.Core - это бесплатная или лицензионная версия?

Raj 28.07.2020 07:53

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

ИЛИ ЖЕ

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

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

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

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

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

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

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

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

var result1 = lst.OrderBy(a=>a.Name);// for ascending order. 
 var result1 = lst.OrderByDescending(a=>a.Name);// for desc order. 

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

Please provide an example for the rest of us.

в 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.

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

install-package System.Linq.Dynamic

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

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

Как я могу использовать его с внутренней сортировкой свойств - например, OrderBy ("Branch.BranchName", "Descending")

devC 23.05.2019 14:08

У меня это работает. Возможно, потому, что этому вопросу 10 лет, а этот более простой метод появился позже.

kosherjellyfish 12.07.2019 09:49

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

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

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

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

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

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

Пару лет спустя я натыкаюсь на это; это сработало для меня, как во сне. У меня есть динамическая сортировка от 1 до 3 свойств, и это работает как мечта. Легко внедрить и без проблем.

Bazïnga 22.03.2020 09:44

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

RoLYroLLs 21.07.2020 00:17

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

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

Другие вопросы по теме