IEnumerable 上的动态 LINQ OrderBy / IQueryable;

发布于 2024-11-08 14:52:41 字数 265 浏览 0 评论 0 原文

我在动态 LINQ 的 VS2008 Examples 中找到了一个示例,它允许您使用类似 SQL 的字符串(例如 OrderBy("Name, Age DESC")) 用于排序。不幸的是,该方法仅适用于 IQueryable。有没有办法在 IEnumerable 上获得此功能?

I found an example in the VS2008 Examples for Dynamic LINQ that allows you to use a SQL-like string (e.g. OrderBy("Name, Age DESC")) for ordering. Unfortunately, the method included only works on IQueryable<T>. Is there any way to get this functionality on IEnumerable<T>?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(24

北城挽邺 2024-11-15 14:52:42

只是偶然发现了这个老东西......

要在没有动态 LINQ 库的情况下执行此操作,您只需要如下代码。这涵盖了最常见的场景,包括嵌套属性。

要使其与 IEnumerable 一起使用,您可以添加一些通过 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等的表达式树可以' t 真正代表动态 查询 - 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);
        }
    }
}

Just stumbled into this oldie...

To do this without the dynamic LINQ library, you just need the code as below. This covers most common scenarios including nested properties.

To get it working with IEnumerable<T> you could add some wrapper methods that go via AsQueryable - but the code below is the core Expression logic needed.

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

Edit: it gets more fun if you want to mix that with dynamic - although note that dynamic only applies to LINQ-to-Objects (expression-trees for ORMs etc can't really represent dynamic queries - MemberExpression doesn't support it). But here's a way to do it with LINQ-to-Objects. Note that the choice of Hashtable is due to favorable locking semantics:

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);
        }
    }
}
帝王念 2024-11-15 14:52:42

太简单了,没有任何复杂性:

  1. 在顶部添加 using System.Linq.Dynamic;
  2. 使用 vehicles = cars.AsQueryable().OrderBy("Make ASC, Year DESC").ToList();

编辑:为了节省一些时间,系统.Linq.Dynamic.Core(System.Linq.Dynamic 已弃用)程序集不是框架的一部分,但可以从 nuget 安装: System.Linq.Dynamic.Core

Too easy without any complication:

  1. Add using System.Linq.Dynamic; at the top.
  2. Use vehicles = vehicles.AsQueryable().OrderBy("Make ASC, Year DESC").ToList();

Edit: to save some time, the System.Linq.Dynamic.Core (System.Linq.Dynamic is deprecated) assembly is not part of the framework, but can be installed from nuget: System.Linq.Dynamic.Core

街角迷惘 2024-11-15 14:52:42

刚刚偶然发现了这个问题。

使用上面的 Marc 的 ApplyOrder 实现,我拼凑了一个扩展方法来处理类似 SQL 的字符串:

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

详细信息可以在此处找到: http://aonnull.blogspot.com/2010/08/dynamic-sql-like-linq-orderby-extension.html

Just stumbled across this question.

Using Marc's ApplyOrder implementation from above, I slapped together an Extension method that handles SQL-like strings like:

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

Details can be found here: http://aonnull.blogspot.com/2010/08/dynamic-sql-like-linq-orderby-extension.html

成熟的代价 2024-11-15 14:52:42

我想使用反射来获取您想要排序的任何属性是可行的:

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

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

I guess it would work to use reflection to get whatever property you want to sort on:

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

Note that using reflection is considerably slower than accessing the property directly, so the performance would have to be investigated.

原谅过去的我 2024-11-15 14:52:42

只是建立在其他人所说的基础上。我发现下面的方法效果很好。

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

Just building on what others have said. I found that the following works quite well.

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;
}
稳稳的幸福 2024-11-15 14:52:42

我试图这样做,但遇到 Kjetil Watnedal 的解决方案 的问题,因为我不使用内联 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>());

请参阅 这篇文章

I was trying to do this but having problems with Kjetil Watnedal's solution because I don't use the inline linq syntax - I prefer method-style syntax. My specific problem was in trying to do dynamic sorting using a custom IComparer.

My solution ended up like this:

Given an IQueryable query like so:

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

And given a run-time sort field argument:

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

The dynamic OrderBy looks like so:

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

And that's using a little helper method called GetReflectedPropertyValue():

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

One last thing - I mentioned that I wanted the OrderBy to use custom IComparer - because I wanted to do Natural sorting.

To do that, I just alter the OrderBy to:

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

See this post for the code for NaturalSortComparer().

手长情犹 2024-11-15 14:52:42

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

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

After a lot of searching this worked for me:

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);
}
懵少女 2024-11-15 14:52:42

我在寻找 Linq multiple orderby 子句时偶然发现了这个问题
也许这就是作者想要的,

具体方法如下:

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

I've stumble this question looking for Linq multiple orderby clauses
and maybe this was what the author was looking for

Here's how to do that:

var query = pets.OrderBy(pet => pet.Name).ThenByDescending(pet => pet.Age);    
十年九夏 2024-11-15 14:52:42

使用动态 linq

只需添加 using System.Linq.Dynamic;

并像这样使用它来排序所有列:

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

Use dynamic linq

just add using System.Linq.Dynamic;

And use it like this to order all your columns:

string sortTypeStr = "ASC"; // or DESC
string SortColumnName = "Age"; // Your column name
query = query.OrderBy($"{SortColumnName} {sortTypeStr}");
狼性发作 2024-11-15 14:52:42

首次安装动态
工具 --> NuGet 包管理器 -->包管理器控制台

install-package System.Linq.Dynamic

添加命名空间 using System.Linq.Dynamic;

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

First Install Dynamic
Tools --> NuGet Package Manager --> Package Manager Console

install-package System.Linq.Dynamic

Add Namespace using System.Linq.Dynamic;

Now you can use OrderBy("Name, Age DESC")

宁愿没拥抱 2024-11-15 14:52:42

您可以添加它:

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 的回答

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

如果您正在处理 Linq to Sql 并且 orderby 是一个表达式树,那么无论如何它都会被转换为 SQL 来执行。

You could add it:

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
}

The GetPropertyValue function is from Kjetil Watnedal's answer

The issue would be why? Any such sort would throw exceptions at run-time, rather than compile time (like D2VIANT's answer).

If you're dealing with Linq to Sql and the orderby is an expression tree it will be converted into SQL for execution anyway.

糖果控 2024-11-15 14:52:42

这是我发现的其他有趣的事情。
如果您的源是 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();

Here's something else I found interesting.
If your source is a DataTable, you can use dynamic sorting without using 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;

reference: http://msdn.microsoft.com/en-us/library/bb669083.aspx (Using DataSetExtensions)

Here is one more way to do it by converting it to a DataView:

DataTable contacts = dataSet.Tables["Contact"];    
DataView view = contacts.AsDataView();    
view.Sort = "LastName desc, FirstName asc";    
bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();
乖乖 2024-11-15 14:52:42

您可以将 IEnumerable 转换为 IQueryable。

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

You can convert the IEnumerable to IQueryable.

items = items.AsQueryable().OrderBy("Name ASC");
太阳男子 2024-11-15 14:52:42

你可以使用这个:

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

You can use this:

        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();
}
森林散布 2024-11-15 14:52:42

替代解决方案使用以下类/接口。它不是真正动态的,但它有效。

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

An alternate solution uses the following class/interface. It's not truly dynamic, but it works.

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;
    }
}
我ぃ本無心為│何有愛 2024-11-15 14:52:42

感谢 Maarten(在 LINQ 中使用 PropertyInfo 对象查询集合 )我得到了这个解决方案:

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

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

干杯

Thanks to Maarten (Query a collection using PropertyInfo object in LINQ) I got this solution:

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

In my case I was working on a "ColumnHeaderMouseClick" (WindowsForm) so just found the specific Column pressed and its correspondent 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();

(be sure to have your column Names matching the object Properties)

Cheers

大海や 2024-11-15 14:52:42

此答案是对需要 @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版本进行绑定,例如Asp.net中的GridView,并有利于排序(你不能使用 IEnumerable 版本进行排序)

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

This answer is a response to the comments that need an example for the solution provided by @John Sheehan - Runscope

Please provide an example for the rest of us.

in DAL (Data Access Layer),

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

Now you can use the IQueryable version to bind, for example GridView in Asp.net and benefit for sorting (you can't sort using IEnumerable version)

I used Dapper as ORM and build IQueryable version and utilized sorting in GridView in asp.net so easy.

我的奇迹 2024-11-15 14:52:42

您可以定义从字符串到 Func<> 的字典。像这样:

Dictionary<string, Func<Item, object>> SortParameters = new Dictionary<string, Func<Item, object>>()
{
    {"Rank", x => x.Rank}
};

并像这样使用它:

yourList.OrderBy(SortParameters["Rank"]);

在这种情况下,您可以按字符串动态排序。

You can define a dictionary from string to Func<> like this :

Dictionary<string, Func<Item, object>> SortParameters = new Dictionary<string, Func<Item, object>>()
{
    {"Rank", x => x.Rank}
};

And use it like this :

yourList.OrderBy(SortParameters["Rank"]);

In this case you can dynamically sort by string.

抽个烟儿 2024-11-15 14:52:42

你可以这样对多个订单进行操作

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

you can do it like this for multiple order by

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]);
    }
}
一曲爱恨情仇 2024-11-15 14:52:42

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

Convert List to IEnumerable or Iquerable, add using System.LINQ.Dynamic namespace, then u can mention the property names in comma seperated string to OrderBy Method which comes by default from System.LINQ.Dynamic.

娜些时光,永不杰束 2024-11-15 14:52:42

我可以使用下面的代码来做到这一点。无需编写又长又复杂的代码。

 protected void sort_array(string field_name, string asc_desc)
        {

            objArrayList= Sort(objArrayList, field_name, asc_desc);
        }

        protected List<ArrayType> Sort(List<ArrayType> input, string property, string asc_desc)
        {
            if (asc_desc == "ASC")
            {

                return input.OrderBy(p => p.GetType()
                                           .GetProperty(property)
                                           .GetValue(p, null)).ToList();
            }
            else
            {
                return input.OrderByDescending(p => p.GetType()
                                               .GetProperty(property)
                                               .GetValue(p, null)).ToList();
            }
        }

I am able to do this with the code below. No need write long and complex code.

 protected void sort_array(string field_name, string asc_desc)
        {

            objArrayList= Sort(objArrayList, field_name, asc_desc);
        }

        protected List<ArrayType> Sort(List<ArrayType> input, string property, string asc_desc)
        {
            if (asc_desc == "ASC")
            {

                return input.OrderBy(p => p.GetType()
                                           .GetProperty(property)
                                           .GetValue(p, null)).ToList();
            }
            else
            {
                return input.OrderByDescending(p => p.GetType()
                                               .GetProperty(property)
                                               .GetValue(p, null)).ToList();
            }
        }
梦醒时光 2024-11-15 14:52:42

如果您使用规范(例如 Ardalis 规范

using Microsoft.EntityFrameworkCore;

namespace TestExtensions;

public static class IQueryableExtensions
{
    public static void ApplyOrder<T>(ISpecificationBuilder<T> query, string propertyName, bool ascendingOrder)
    {
        if (ascendingOrder)
            query.OrderBy(T => EF.Property<object>(T!, propertyName));
        else
            query.OrderByDescending(T => EF.Property<object>(T!, propertyName));
    }
}

If you are using Specification (such as Ardalis Specification)

using Microsoft.EntityFrameworkCore;

namespace TestExtensions;

public static class IQueryableExtensions
{
    public static void ApplyOrder<T>(ISpecificationBuilder<T> query, string propertyName, bool ascendingOrder)
    {
        if (ascendingOrder)
            query.OrderBy(T => EF.Property<object>(T!, propertyName));
        else
            query.OrderByDescending(T => EF.Property<object>(T!, propertyName));
    }
}
尘曦 2024-11-15 14:52:42

使用 Net6 和 EF

.AsQueryable().OrderBy((ColumnOrder.Column, ColumnOrder.Dir));
                            

With Net6 and EF

.AsQueryable().OrderBy((ColumnOrder.Column, ColumnOrder.Dir));
                            
下雨或天晴 2024-11-15 14:52:42
var result1 = lst.OrderBy(a=>a.Name);// for ascending order. 
 var result1 = lst.OrderByDescending(a=>a.Name);// for desc order. 
var result1 = lst.OrderBy(a=>a.Name);// for ascending order. 
 var result1 = lst.OrderByDescending(a=>a.Name);// for desc order. 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文