如何使用动态字符串参数执行OrderBy?

Luc*_*oli 53 linq sql-order-by

我想做这个:

var orderBy = "Nome, Cognome desc";

var timb = time.Timbratures.Include("Anagrafica_Dipendente")
    .Where(p => p.CodDipendente == 1);

if(orderBy != "")
    timb = timb.OrderBy(orderBy);
Run Code Online (Sandbox Code Playgroud)

是否有OrderBy可用的超载接受字符串参数?

Mar*_*age 67

如果您使用简单的LINQ到对象并且不想依赖外部库,那么实现您想要的并不困难.

OrderBy()子句接受一个Func<TSource, TKey>从源元素获取排序键的子句.您可以在OrderBy()子句外定义函数:

Func<Item, Object> orderByFunc = null;
Run Code Online (Sandbox Code Playgroud)

然后,您可以根据排序条件将其分配给不同的值:

if (sortOrder == SortOrder.SortByName)
  orderByFunc = item => item.Name;
else if (sortOrder == SortOrder.SortByRank)
  orderByFunc = item => item.Rank;
Run Code Online (Sandbox Code Playgroud)

然后你可以排序:

var sortedItems = items.OrderBy(orderByFunc);
Run Code Online (Sandbox Code Playgroud)

此示例假定源类型Item具有属性NameRank.

请注意,在此示例TKeyObject,不限制可以排序的属性类型.如果func返回一个值类型(如Int32),它将在排序时被装箱,这有点效率低下.如果您可以约束TKey到特定值类型,则可以解决此问题.

  • 干净,很棒的回答,TKey让我感到困惑,我认为它被定义在链条的较高位置并且不能如此容易地盖章.我一直在使用DynLinq分辨率.谢谢你. (3认同)
  • @YuvalA.:如果其中一个属性是数字(这包括`DateTime.Ticks`),您可以否定值以相反的顺序排序.否则你可以有条件地使用`OrderBy`或`OrderByDescending`:`var sortedItems = reverse?items.OrderByDescending(orderByFunc):items.OrderBy(orderByFunc)`. (2认同)

Mik*_*ney 42

绝对.您可以使用Scott Guthrie博客上的LINQ Dynamic Query Library .CodePlex上还有一个更新版本.

它允许您通过传入字符串参数来创建OrderBy子句,Where子句以及其他所有内容.它非常适合创建用于排序/过滤网格等的通用代码.

var result = data
    .Where(/* ... */)
    .Select(/* ... */)
    .OrderBy("Foo asc");

var query = DbContext.Data
    .Where(/* ... */)
    .Select(/* ... */)
    .OrderBy("Foo ascending");
Run Code Online (Sandbox Code Playgroud)

  • 这有什么安全问题吗?与LINQ注入一样,允许用户输入"OrderBy"字段是不安全的吗?例如`.OrderBy("UserInput ascending").` (4认同)
  • @jthomperoo,您应该始终验证此处的用户输入。这是微不足道的。 (2认同)

Chr*_*han 29

codeConcussion的另一个解决方案(/sf/answers/508577611/)

var param = "Address";    
var pi = typeof(Student).GetProperty(param);    
var orderByAddress = items.OrderBy(x => pi.GetValue(x, null));
Run Code Online (Sandbox Code Playgroud)

  • 这在Linq to Entity Framework中不起作用,因为它抛出此错误`LINQ to Entities无法识别方法'System.Object GetValue(System.Object,System.Object [])'方法,并且此方法无法转换为商店expression.` (4认同)
  • 如果他想对结果进行分页,这不是一件好事.这样的分页将在OrderBy之后带有Skip()和Take()...... (2认同)

nia*_*her 14

您不需要外部库.以下代码适用于LINQ to SQL /实体.

    /// <summary>
    /// Sorts the elements of a sequence according to a key and the sort order.
    /// </summary>
    /// <typeparam name="TSource">The type of the elements of <paramref name="query" />.</typeparam>
    /// <param name="query">A sequence of values to order.</param>
    /// <param name="key">Name of the property of <see cref="TSource"/> by which to sort the elements.</param>
    /// <param name="ascending">True for ascending order, false for descending order.</param>
    /// <returns>An <see cref="T:System.Linq.IOrderedQueryable`1" /> whose elements are sorted according to a key and sort order.</returns>
    public static IQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> query, string key, bool ascending = true)
    {
        if (string.IsNullOrWhiteSpace(key))
        {
            return query;
        }

        var lambda = (dynamic)CreateExpression(typeof(TSource), key);

        return ascending 
            ? Queryable.OrderBy(query, lambda) 
            : Queryable.OrderByDescending(query, lambda);
    }

    private static LambdaExpression CreateExpression(Type type, string propertyName)
    {
        var param = Expression.Parameter(type, "x");

        Expression body = param;
        foreach (var member in propertyName.Split('.'))
        {
            body = Expression.PropertyOrField(body, member);
        }

        return Expression.Lambda(body, param);
    }
Run Code Online (Sandbox Code Playgroud)

(CreateExpression/sf/answers/1134603431/复制)


Kas*_*oma 8

最简单和最好的解决方案:

mylist.OrderBy(s => s.GetType().GetProperty("PropertyName").GetValue(s));
Run Code Online (Sandbox Code Playgroud)

  • 这太棒了 (2认同)
  • 只需添加 PropertyName 区分大小写,因此如果您要从数据库获取数据,那么您可能需要添加 `BindingFlags.IgnoreCase` (2认同)
  • 不适用于 Linq toEntity EF core。 (2认同)

Ste*_*ven 6

这里看看这个博客.它描述了一种方法,通过定义一个EntitySorter<T>.

它允许您IEntitySorter<T>传入您的服务方法并使用它如下:

public static Person[] GetAllPersons(IEntitySorter<Person> sorter)
{
    using (var db = ContextFactory.CreateContext())
    {
        IOrderedQueryable<Person> sortedList = sorter.Sort(db.Persons);

        return sortedList.ToArray();
    }
}
Run Code Online (Sandbox Code Playgroud)

你可以创建一个EntitiySorter这样的:

IEntitySorter<Person> sorter = EntitySorter<Person>
    .OrderBy(p => p.Name)
    .ThenByDescending(p => p.Id);
Run Code Online (Sandbox Code Playgroud)

或者像这样:

var sorter = EntitySorter<Person>
     .OrderByDescending("Address.City")
     .ThenBy("Id");
Run Code Online (Sandbox Code Playgroud)


Gus*_*ler 6

我是这样做的:

using System.Linq.Expressions;

namespace System.Linq
{
    public static class LinqExtensions
    {

        public static IOrderedQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string field, string dir = "asc")
        {
            // parametro => expressão
            var parametro = Expression.Parameter(typeof(TSource), "r");
            var expressao = Expression.Property(parametro, field);
            var lambda = Expression.Lambda(expressao, parametro); // r => r.AlgumaCoisa
            var tipo = typeof(TSource).GetProperty(field).PropertyType;

            var nome = "OrderBy";
            if (string.Equals(dir, "desc", StringComparison.InvariantCultureIgnoreCase))
            {
                nome = "OrderByDescending";
            }
            var metodo = typeof(Queryable).GetMethods().First(m => m.Name == nome && m.GetParameters().Length == 2);
            var metodoGenerico = metodo.MakeGenericMethod(new[] { typeof(TSource), tipo });
            return metodoGenerico.Invoke(source, new object[] { source, lambda }) as IOrderedQueryable<TSource>;

        }

        public static IOrderedQueryable<TSource> ThenBy<TSource>(this IOrderedQueryable<TSource> source, string field, string dir = "asc")
        {
            var parametro = Expression.Parameter(typeof(TSource), "r");
            var expressao = Expression.Property(parametro, field);
            var lambda = Expression.Lambda<Func<TSource, string>>(expressao, parametro); // r => r.AlgumaCoisa
            var tipo = typeof(TSource).GetProperty(field).PropertyType;

            var nome = "ThenBy";
            if (string.Equals(dir, "desc", StringComparison.InvariantCultureIgnoreCase))
            {
                nome = "ThenByDescending";
            }

            var metodo = typeof(Queryable).GetMethods().First(m => m.Name == nome && m.GetParameters().Length == 2);
            var metodoGenerico = metodo.MakeGenericMethod(new[] { typeof(TSource), tipo });
            return metodoGenerico.Invoke(source, new object[] { source, lambda }) as IOrderedQueryable<TSource>;
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

使用 :

example.OrderBy("Nome", "desc").ThenBy("other")
Run Code Online (Sandbox Code Playgroud)

像这样工作:

example.OrderByDescending(r => r.Nome).ThenBy(r => r.other)
Run Code Online (Sandbox Code Playgroud)


Nic*_*ray 5

您需要使用 LINQ动态查询库才能在运行时传递参数,

这将允许像这样的 linq 语句

string orderedBy = "Description";
var query = (from p in products
            orderby(orderedBy)
            select p);
Run Code Online (Sandbox Code Playgroud)