使用属性名称作为字符串排序

max*_*ntz 3 c# linq asp.net entity-framework-core asp.net-core

我希望我的Web API能够通过字符串参数对其输出进行排序,例如:

http://myapi.com/api/people?skip=0&take=50&orderBy=lastName&descending=true.

因为我也有分页支持(skiptake我的API),我想orderBydescending参数直接应用于SQL查询,使正确的结果来自于数据库.

但是,在执行此操作时,在尝试将参数orderBy与我希望通过字符串比较排序的类的实际属性进行匹配时,代码可能变得非常难以管理.

我找到了一个解决方案,该解决方案应该与LINQ to Entities一起工作,因此也适用于新的EF7,但是当我尝试使用新的Core CLR编译此代码时,我收到以下消息:

错误CS1503参数2:无法从'System.Linq.Expressions.Expression>'转换为'string'

失败的解决方案的代码是OrderBy<T>方法:

public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName)
{
    return source.OrderBy(ToLambda<T>(propertyName));
}
Run Code Online (Sandbox Code Playgroud)

似乎新的Core CLR不支持这种尝试.有没有其他方法可以使解决方案与新的CLR一起使用?如果不是,我还有什么其他选择来使用EF7进行排序而不会导致无数ifswitch语句将输入字符串与属性名称进行比较?

Jon*_*nan 8

链接的解决方案使用"Expression.Convert",大部分时间都不能与LINQ to Entities一起使用.

这是一个有效的扩展方法:

public static IOrderedQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string propertyName)
{
    // LAMBDA: x => x.[PropertyName]
    var parameter = Expression.Parameter(typeof(TSource), "x");
    Expression property = Expression.Property(parameter, propertyName);
    var lambda = Expression.Lambda(property, parameter);

    // REFLECTION: source.OrderBy(x => x.Property)
    var orderByMethod = typeof(Queryable).GetMethods().First(x => x.Name == "OrderBy" && x.GetParameters().Length == 2);
    var orderByGeneric = orderByMethod.MakeGenericMethod(typeof(TSource), property.Type);
    var result = orderByGeneric.Invoke(null, new object[] { source, lambda });

    return (IOrderedQueryable<TSource>)result;
}
Run Code Online (Sandbox Code Playgroud)

免责声明:我是GitHub上EF +项目的所有者.

您可以在我的存储库中找到按属性名称排序的其他方法:GitHub

  • OrderByDescending
  • ThenBy
  • ThenByDescending
  • AddOrAppendOrderBy
  • AddOrAppendOrderByDescending

编辑:回答子问题

是否可以使用类似的东西对导航属性进行排序,例如属性名称"NavigationProperty.PropertyName"

是的,您可以拆分字符串并循环以使用属性路径创建表达式或使用实际表达式求值程序.

免责声明:我是Eval-Expressions.NET项目的所有者

该库允许您动态执行所有LINQ方法.

请参阅:LINQ Dynamic

var result = list.OrderByDynamic(x => "NavigationProperty.PropertyName");
Run Code Online (Sandbox Code Playgroud)