如何在实体框架中动态构造按表达式排序
本文关键字:表达式 排序 动态 实体 框架 | 更新日期: 2023-09-27 18:31:07
我使用以下方法来构造 Order By Expression。原始来源
它真的很光滑。缺点是它仅在属性为字符串类型时才有效。
如何使其接受不同的属性类型,而无需为不同的数据类型创建一堆方法?
public static bool PropertyExists<T>(string propertyName)
{
return typeof (T).GetProperty(propertyName, BindingFlags.IgnoreCase |
BindingFlags.Public | BindingFlags.Instance) != null;
}
public static Expression<Func<T, string>> GetPropertyExpression<T>(string propertyName)
{
if (typeof(T).GetProperty(propertyName, BindingFlags.IgnoreCase |
BindingFlags.Public | BindingFlags.Instance) == null)
{
return null;
}
var paramterExpression = Expression.Parameter(typeof(T));
return (Expression<Func<T, string>>)Expression.Lambda(
Expression.PropertyOrField(paramterExpression, propertyName), paramterExpression);
}
用法
// orderBy can be either Name or City.
if (QueryHelper.PropertyExists<Club>(orderBy))
{
var orderByExpression = QueryHelper.GetPropertyExpression<Club>(orderBy);
clubQuery = clubQuery.OrderBy(orderByExpression);
}
else
{
clubQuery = clubQuery.OrderBy(c => c.Id);
}
问题
public class Club
{
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
public DateTime CreateDate { get; set; } <= this won't work
}
我目前的方法(太多的if语句)
public static Expression<Func<TSource, TKey>>
GetPropertyExpression<TSource, TKey>(string propertyName)
{
if (typeof (TSource).GetProperty(propertyName, BindingFlags.IgnoreCase |
BindingFlags.Public | BindingFlags.Instance) == null)
{
return null;
}
var paramterExpression = Expression.Parameter(typeof (TSource));
return (Expression<Func<TSource, TKey>>)
Expression.Lambda(Expression.PropertyOrField(
paramterExpression, propertyName), paramterExpression);
}
缺点是我最终为每个数据类型提供了很多 if 语句。
if (QueryHelper.PropertyExists<Club>(orderBy))
{
if(orderBy == "CreateDate")
{
var orderByExpression = GetPropertyExpression<Club, DateTime>(orderBy);
...
}
else if(orderBy == "Name" || orderBy == "City")
{
var orderByExpression = GetPropertyExpression<Club, string>(orderBy);
...
}
...
}
else
{
clubQuery = clubQuery.OrderBy(c => c.Id);
}
我在Jon Skeet的旧答案的帮助下找到了解决方案。
public static class QueryHelper
{
private static readonly MethodInfo OrderByMethod =
typeof (Queryable).GetMethods().Single(method =>
method.Name == "OrderBy" && method.GetParameters().Length == 2);
private static readonly MethodInfo OrderByDescendingMethod =
typeof (Queryable).GetMethods().Single(method =>
method.Name == "OrderByDescending" && method.GetParameters().Length == 2);
public static bool PropertyExists<T>(this IQueryable<T> source, string propertyName)
{
return typeof(T).GetProperty(propertyName, BindingFlags.IgnoreCase |
BindingFlags.Public | BindingFlags.Instance) != null;
}
public static IQueryable<T> OrderByProperty<T>(
this IQueryable<T> source, string propertyName)
{
if (typeof (T).GetProperty(propertyName, BindingFlags.IgnoreCase |
BindingFlags.Public | BindingFlags.Instance) == null)
{
return null;
}
ParameterExpression paramterExpression = Expression.Parameter(typeof (T));
Expression orderByProperty = Expression.Property(paramterExpression, propertyName);
LambdaExpression lambda = Expression.Lambda(orderByProperty, paramterExpression);
MethodInfo genericMethod =
OrderByMethod.MakeGenericMethod(typeof (T), orderByProperty.Type);
object ret = genericMethod.Invoke(null, new object[] {source, lambda});
return (IQueryable<T>) ret;
}
public static IQueryable<T> OrderByPropertyDescending<T>(
this IQueryable<T> source, string propertyName)
{
if (typeof (T).GetProperty(propertyName, BindingFlags.IgnoreCase |
BindingFlags.Public | BindingFlags.Instance) == null)
{
return null;
}
ParameterExpression paramterExpression = Expression.Parameter(typeof (T));
Expression orderByProperty = Expression.Property(paramterExpression, propertyName);
LambdaExpression lambda = Expression.Lambda(orderByProperty, paramterExpression);
MethodInfo genericMethod =
OrderByDescendingMethod.MakeGenericMethod(typeof (T), orderByProperty.Type);
object ret = genericMethod.Invoke(null, new object[] {source, lambda});
return (IQueryable<T>) ret;
}
}
用法
string orderBy = "Name";
if (query.PropertyExists(orderBy))
{
query = query.OrderByProperty(orderBy);
- OR -
query = query.OrderByPropertyDescending(orderBy);
}
选项 1 :这可以使用表达式完成:检查此示例
public static IQueryable<T> OrderByPropertyOrField<T>(this IQueryable<T> queryable, string propertyOrFieldName, bool ascending = true)
{
var elementType = typeof (T);
var orderByMethodName = ascending ? "OrderBy" : "OrderByDescending";
var parameterExpression = Expression.Parameter(elementType);
var propertyOrFieldExpression = Expression.PropertyOrField(parameterExpression, propertyOrFieldName);
var selector = Expression.Lambda(propertyOrFieldExpression, parameterExpression);
var orderByExpression = Expression.Call(typeof (Queryable), orderByMethodName,
new[] {elementType, propertyOrFieldExpression.Type}, queryable.Expression, selector);
return queryable.Provider.CreateQuery<T>(orderByExpression);
}
选项 2(如果使用 EF 核心):
public static IQueryable<TEntity> ApplyOrderBy<TEntity>(
this IQueryable<TEntity> query, string? orderBy, string orderDirection)
{
if (orderBy is null) return query;
query = orderDirection == "Asc"
? query.OrderBy(p => EF.Property<TEntity>(p!, orderBy))
: query.OrderByDescending(p => EF.Property<TEntity>(p!, orderBy));
return query;
}
恕
我直言,我有一个更简单的解决方案:
public static IOrderedQueryable<TSource> Sort<TSource>(this IQueryable<TSource> source, bool ascending , string sortingProperty)
{
if (ascending)
return source.OrderBy(item => item.GetReflectedPropertyValue(sortingProperty));
else
return source.OrderByDescending(item => item.GetReflectedPropertyValue(sortingProperty));
}
private static object GetReflectedPropertyValue(this object subject, string field)
{
return subject.GetType().GetProperty(field).GetValue(subject, null);
}
用法是:
myQueryableCollection.Sort(ascending: true, "Name")
当然,PropertyExist()
助手是一个很好的补充......