英文:
I have a problem with dynamic sorting, it doesn't work properly
问题
我有一个用来动态使用OrderBy()
的函数:
internal static Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> GetOrderBy(List<(string, bool)> orderColumnsAndIsDesc)
{
bool IsFirst = true;
MethodCallExpression resultExp = null;
string methodName;
LambdaExpression finalLambda = null;
foreach (var item in orderColumnsAndIsDesc)
{
string prop = item.Item1;
string orderType = item.Item2 == true ? "asc" : "desc";
Type typeQueryable = typeof(IQueryable<TEntity>);
ParameterExpression argQueryable = Expression.Parameter(typeQueryable, "p");
var outerExpression = Expression.Lambda(argQueryable, argQueryable);
IQueryable<TEntity> query = new List<TEntity>().AsQueryable<TEntity>();
Type type = typeof(TEntity);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
PropertyInfo pi = type.GetProperty(prop, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
LambdaExpression lambda = Expression.Lambda(expr, arg);
if (IsFirst)
{
methodName = orderType == "asc" ? "OrderBy" : "OrderByDescending";
resultExp =
Expression.Call(typeof(Queryable), methodName, new Type[] { typeof(TEntity), type }, outerExpression.Body, Expression.Quote(lambda));
}
else
{
methodName = orderType == "asc" ? "ThenBy" : "ThenByDescending";
resultExp =
Expression.Call(typeof(Queryable), methodName, new Type[] { typeof(TEntity), type }, resultExp, Expression.Quote(lambda));
}
finalLambda = Expression.Lambda(resultExp, argQueryable);
IsFirst = false;
}
return (Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>)finalLambda.Compile();
}
当只有一个项目要排序时,它可以正常工作,但当有多个项目时,它会出错。错误信息如下:
System.InvalidOperationException: '从范围引用的类型为 'System.Linq.IQueryable'1[CMS.Data.Models.Category]' 的变量 'p' 未定义'
我不知道如何修复它,请帮助我。
英文:
I have a function that I use to OrderBy()
dynamically:
internal static Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> GetOrderBy(List<(string, bool)> orderColumnsAndIsDesc)
{
bool IsFirst = true;
MethodCallExpression resultExp = null;
string methodName;
LambdaExpression finalLambda = null;
foreach (var item in orderColumnsAndIsDesc)
{
string prop = item.Item1;
string orderType = item.Item2 == true ? "asc" : "desc";
Type typeQueryable = typeof(IQueryable<TEntity>);
ParameterExpression argQueryable = Expression.Parameter(typeQueryable, "p");
var outerExpression = Expression.Lambda(argQueryable, argQueryable);
IQueryable<TEntity> query = new List<TEntity>().AsQueryable<TEntity>();
Type type = typeof(TEntity);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
PropertyInfo pi = type.GetProperty(prop, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
LambdaExpression lambda = Expression.Lambda(expr, arg);
if (IsFirst)
{
methodName = orderType == "asc" ? "OrderBy" : "OrderByDescending";
resultExp =
Expression.Call(typeof(Queryable), methodName, new Type[] { typeof(TEntity), type }, outerExpression.Body, Expression.Quote(lambda));
}
else
{
methodName = orderType == "asc" ? "ThenBy" : "ThenByDescending";
resultExp =
Expression.Call(typeof(Queryable), methodName, new Type[] { typeof(TEntity), type }, resultExp, Expression.Quote(lambda));
}
finalLambda = Expression.Lambda(resultExp, argQueryable);
IsFirst = false;
}
return (Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>)finalLambda.Compile();
}
It works correctly when there is one item to sort, but it gives an error when there is more than one item. its error:
> System.InvalidOperationException: 'variable 'p' of type 'System.Linq.IQueryable'1[CMS.Data.Models.Category]' referenced from scope '', but it is not defined'
I don't know how to fix it, please help me.
答案1
得分: 1
请试试这个:
public static class IEnumerableExtensions {
public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> source, params Tuple<string, bool>[] sortDefinitions)
=> OrderBy<T>(source, sortDefinitions.Select(i => new Tuple<Func<T, object>, bool>(GetPropertyLambda<T>(i.Item1), i.Item2)).ToArray());
//you can try use lambda direcly in params
public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> source, params Tuple<Func<T, object>, bool>[] sortDefinitions) {
if(source?.Any() ?? false) {
var items = source;
foreach(var sortDefinition in sortDefinitions)
if(items is IOrderedEnumerable<T> ordered)
//thenby
items = sortDefinition.Item2 ? ordered.ThenBy(sortDefinition.Item1) : ordered.ThenByDescending(sortDefinition.Item1);
else
items = sortDefinition.Item2 ? items.OrderBy(sortDefinition.Item1) : items.OrderByDescending(sortDefinition.Item1);
return items;
} else
return source;
}
//based on your code
private static Func<T, object> GetPropertyLambda<T>(string propertyName) {
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
PropertyInfo pi = type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
expr = Expression.Property(expr, pi);
return (Func<T, object>) Expression.Lambda(expr, arg).Compile();
}
}
英文:
Try this:
public static class IEnumerableExtensions {
public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> source, params Tuple<string, bool>[] sortDefinitions)
=> OrderBy<T>(source, sortDefinitions.Select(i => new Tuple<Func<T, object>, bool>(GetPropertyLambda<T>(i.Item1), i.Item2)).ToArray());
//you can try use lambda direcly in params
public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> source, params Tuple<Func<T, object>, bool>[] sortDefinitions) {
if(source?.Any() ?? false) {
var items = source;
foreach(var sortDefinition in sortDefinitions)
if(items is IOrderedEnumerable<T> ordered)
//thenby
items = sortDefinition.Item2 ? ordered.ThenBy(sortDefinition.Item1) : ordered.ThenByDescending(sortDefinition.Item1);
else
items = sortDefinition.Item2 ? items.OrderBy(sortDefinition.Item1) : items.OrderByDescending(sortDefinition.Item1);
return items;
} else
return source;
}
//based on your code
private static Func<T, object> GetPropertyLambda<T>(string propertyName) {
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
PropertyInfo pi = type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
expr = Expression.Property(expr, pi);
return (Func<T, object>) Expression.Lambda(expr, arg).Compile();
}
}
答案2
得分: 0
如果您使用扩展方法ApplyOrderBy,您可以按照以下方式编写您的函数:
public static Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> GetOrderBy<TEntity>(
IEnumerable<(string, bool)> order)
{
return query => (IOrderedQueryable<TEntity>)query
.ApplyOrderBy(order.Select(t => Tuple.Create(t.Item1, t.Item2)));
}
英文:
If you use extension method ApplyOrderBy, you can write your function in the following way:
public static Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> GetOrderBy<TEntity>(
IEnumerable<(string, bool)> order)
{
return query => (IOrderedQueryable<TEntity>)query
.ApplyOrderBy(order.Select(t => Tuple.Create(t.Item1, t.Item2)));
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论