【问题标题】:Call OrderBy() with a field name as a string [duplicate]使用字段名称作为字符串调用 OrderBy() [重复]
【发布时间】:2015-08-18 00:30:14
【问题描述】:

我正在使用 .NET 4.51、EF 6

我对存储库层进行了多次调用,我需要在单个字段上按升序或降序进行一些基本排序,例如:

GetAllList() 的结果是List<T>。现在不幸的是,我必须排序的Id 字段并不总是称为IdText 字段也不是。它们可以是其他东西,例如MyIdSomeTextField 等等。

所以我想知道是否有一种方法可以通过为字段名称提供类似以下内容的字符串来执行 OrderBy()OrderByDescending() 子句:

_Repository.GetAllList().OrderBy(r => r."SomeTextField")

通过这种方式,我可以将所有这些代码移到一个通用方法中。

非常感谢任何指针。

【问题讨论】:

  • 唯一的方法是你可以用反射来做你想做的事。您应该能够使用Func<T, TKey> 并在不使用字符串的情况下完成同样的事情,就像LINQ 一样
  • 如果对象是来自 DataTable 的 DataRow(),则可以。

标签: c# linq entity-framework


【解决方案1】:

这将起作用:

public static class LinqExtensions 
{
    private static PropertyInfo GetPropertyInfo(Type objType, string name)
    {
        var properties = objType.GetProperties();
        var matchedProperty = properties.FirstOrDefault (p => p.Name == name);
        if (matchedProperty == null)
            throw new ArgumentException("name");

        return matchedProperty;
    }
    private static LambdaExpression GetOrderExpression(Type objType, PropertyInfo pi)
    {
        var paramExpr = Expression.Parameter(objType);
        var propAccess = Expression.PropertyOrField(paramExpr, pi.Name);
        var expr = Expression.Lambda(propAccess, paramExpr);
        return expr;
    }

    public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> query, string name)
    {
        var propInfo = GetPropertyInfo(typeof(T), name);
        var expr = GetOrderExpression(typeof(T), propInfo);

        var method = typeof(Enumerable).GetMethods().FirstOrDefault(m => m.Name == "OrderBy" && m.GetParameters().Length == 2);
        var genericMethod = method.MakeGenericMethod(typeof(T), propInfo.PropertyType);     
        return (IEnumerable<T>) genericMethod.Invoke(null, new object[] { query, expr.Compile() });
    }

    public static IQueryable<T> OrderBy<T>(this IQueryable<T> query, string name)
    {
        var propInfo = GetPropertyInfo(typeof(T), name);
        var expr = GetOrderExpression(typeof(T), propInfo);

        var method = typeof(Queryable).GetMethods().FirstOrDefault(m => m.Name == "OrderBy" && m.GetParameters().Length == 2);
        var genericMethod = method.MakeGenericMethod(typeof(T), propInfo.PropertyType);     
        return (IQueryable<T>) genericMethod.Invoke(null, new object[] { query, expr });
    }
}

测试:

var r = new List<temp> { 
    new temp { a = 5 }, 
    new temp { a = 1 }, 
    new temp { a = 15 }
}.OrderBy("a");

给出正确的结果 (1, 5, 15) - 并提供延迟执行以供您与 EF 一起使用

如果需要,您将需要实现重载。

【讨论】:

  • 非常感谢。它也适用于 EF core 3.1。
  • 这是最简单最直观的代码,我见过关于这个主题的,易于扩展。
【解决方案2】:

它必须是一个字符串吗?为什么不直接创建一个将Func 键选择器作为参数的方法。

public List<T> GetAllListOrdered<T,TProp>(SimpleOrderingDirectionEnum direction, Func<T,TProp> keySelector)
{
    return direction == SimpleOrderingDirectionEnum.Ascending ? _Repository.GetAllList().OrderBy(keySelector).ToList() : _Repository.GetAllList().OrderByDescending(keySelector).ToList();
}

然后这样称呼它

Func<ObjectToSortType, ObjectPropertyToSortBy> keySelector = r => r.Id;
GetAllListOrdered(SimpleOrderingDirectionEnum.Ascending, keySelector);

【讨论】:

  • 如果我们使用 linq to entity,这不会转换为 sql order by,因为 linq to entity 需要表达式树。请改用Expression&lt;Func&lt;T,TProp&gt;&gt;
【解决方案3】:

如果 Rob 的回答对您来说还不够。试试 Linq 动态。 http://dynamiclinq.azurewebsites.net/

using System.Linq.Dynamic; //Import the Dynamic LINQ library

//The standard way, which requires compile-time knowledge
//of the data model
var result = myQuery
    .Where(x => x.Field1 == "SomeValue")
    .Select(x => new { x.Field1, x.Field2 });

//The Dynamic LINQ way, which lets you do the same thing
//without knowing the data model before hand
var result = myQuery
    .Where("Field1=\"SomeValue\"")
    .Select("new (Field1, Field2)");

【讨论】:

  • 我曾考虑过这一点,但想在不引入另一个库的情况下做点什么。然而,我最终还是在我的首选解决方案中使用了它,
【解决方案4】:

谢谢大家。 Rob,您的解决方案与我最终的解决方案非常接近。

根据您的见解,我进行了更多搜索,并在此处Dynamic LINQ OrderBy on IEnumerable<T>(第二篇文章)找到了 Marc Gravel 的答案。

它添加了动态作为额外的奖励。

【讨论】:

  • 或者只使用stackoverflow.com/a/15389224 有一个 System.Linq.Dynamic nuget 库,它有 .OrderBy(string ordering) 所以你可以像这样使用它:list.OrderBy("firstName asc")
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-13
  • 2011-07-13
  • 2011-12-17
  • 1970-01-01
  • 2012-05-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多