使用reflection和expression-trees可以提供参数然后调用OrderBy函数,而不是返回Expression<Func<Task, T>>然后调用OrderBy。
请注意,OrderBy 是一种扩展方法,并已在 System.Linq.Enumarable 和 System.Linq.Queryable 类中实现。第一个用于linq-to-objects,后者用于linq-to-entities。 entity-framework 需要查询的表达式树才能将其转换为 SQL 命令。所以我们使用Queryable 实现。
可以通过扩展方法来完成(添加为cmets的解释):
public static IOrderedQueryable<TSource> OrderBy<TSource>(
this IQueryable<TSource> query, string propertyName)
{
var entityType = typeof(TSource);
//Create x=>x.PropName
var propertyInfo = entityType.GetProperty(propertyName);
ParameterExpression arg = Expression.Parameter(entityType, "x");
MemberExpression property = Expression.Property(arg, propertyName);
var selector = Expression.Lambda(property, new ParameterExpression[] { arg });
//Get System.Linq.Queryable.OrderBy() method.
var enumarableType = typeof(System.Linq.Queryable);
var method = enumarableType.GetMethods()
.Where(m => m.Name == "OrderBy" && m.IsGenericMethodDefinition)
.Where(m =>
{
var parameters = m.GetParameters().ToList();
//Put more restriction here to ensure selecting the right overload
return parameters.Count == 2;//overload that has 2 parameters
}).Single();
//The linq's OrderBy<TSource, TKey> has two generic types, which provided here
MethodInfo genericMethod = method
.MakeGenericMethod(entityType, propertyInfo.PropertyType);
/*Call query.OrderBy(selector), with query and selector: x=> x.PropName
Note that we pass the selector as Expression to the method and we don't compile it.
By doing so EF can extract "order by" columns and generate SQL for it.*/
var newQuery = (IOrderedQueryable<TSource>)genericMethod
.Invoke(genericMethod, new object[] { query, selector });
return newQuery;
}
现在您可以像其他任何重载一样调用 OrderBy 的重载。
例如:
var cheapestItems = _context.Items.OrderBy("Money").Take(10).ToList();
翻译为:
SELECT TOP (10) {coulmn names} FROM [dbo].[Items] AS [Extent1]
ORDER BY [Extent1].[Money] ASC
此方法可用于定义 OrderBy 和 OrderByDescending 方法的所有重载以具有 string 属性选择器。