我在动态排序方面遇到了问题,它不能正常工作。

huangapple go评论76阅读模式
英文:

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&lt;IQueryable&lt;TEntity&gt;, IOrderedQueryable&lt;TEntity&gt;&gt; GetOrderBy(List&lt;(string, bool)&gt; 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 ? &quot;asc&quot; : &quot;desc&quot;;
        Type typeQueryable = typeof(IQueryable&lt;TEntity&gt;);
        ParameterExpression argQueryable = Expression.Parameter(typeQueryable, &quot;p&quot;);
        var outerExpression = Expression.Lambda(argQueryable, argQueryable);
        IQueryable&lt;TEntity&gt; query = new List&lt;TEntity&gt;().AsQueryable&lt;TEntity&gt;();
        Type type = typeof(TEntity);
        ParameterExpression arg = Expression.Parameter(type, &quot;x&quot;);
        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 == &quot;asc&quot; ? &quot;OrderBy&quot; : &quot;OrderByDescending&quot;;
            resultExp =
            Expression.Call(typeof(Queryable), methodName, new Type[] { typeof(TEntity), type }, outerExpression.Body, Expression.Quote(lambda));
        }
        else
        {
            methodName = orderType == &quot;asc&quot; ? &quot;ThenBy&quot; : &quot;ThenByDescending&quot;;
            resultExp =
           Expression.Call(typeof(Queryable), methodName, new Type[] { typeof(TEntity), type }, resultExp, Expression.Quote(lambda));
        }
		
        finalLambda = Expression.Lambda(resultExp, argQueryable);
		
        IsFirst = false;
    }
   
    return (Func&lt;IQueryable&lt;TEntity&gt;, IOrderedQueryable&lt;TEntity&gt;&gt;)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&lt;T&gt; OrderBy&lt;T&gt;(this IEnumerable&lt;T&gt; source, params Tuple&lt;string, bool&gt;[] sortDefinitions)
        =&gt; OrderBy&lt;T&gt;(source, sortDefinitions.Select(i =&gt; new Tuple&lt;Func&lt;T, object&gt;, bool&gt;(GetPropertyLambda&lt;T&gt;(i.Item1), i.Item2)).ToArray());

    //you can try use lambda direcly in params
    public static IEnumerable&lt;T&gt; OrderBy&lt;T&gt;(this IEnumerable&lt;T&gt; source, params Tuple&lt;Func&lt;T, object&gt;, bool&gt;[] sortDefinitions) {
        if(source?.Any() ?? false) {
            var items = source;
            foreach(var sortDefinition in sortDefinitions)
                if(items is IOrderedEnumerable&lt;T&gt; 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&lt;T, object&gt; GetPropertyLambda&lt;T&gt;(string propertyName) {
        Type type = typeof(T);
        ParameterExpression arg = Expression.Parameter(type, &quot;x&quot;);
        Expression expr = arg;
        PropertyInfo pi = type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
        expr = Expression.Property(expr, pi);
        return (Func&lt;T, object&gt;) 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&lt;IQueryable&lt;TEntity&gt;, IOrderedQueryable&lt;TEntity&gt;&gt; GetOrderBy&lt;TEntity&gt;(
	IEnumerable&lt;(string, bool)&gt; order)
{
	return query =&gt; (IOrderedQueryable&lt;TEntity&gt;)query
       .ApplyOrderBy(order.Select(t =&gt; Tuple.Create(t.Item1, t.Item2)));
}

huangapple
  • 本文由 发表于 2023年1月9日 16:23:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/75054683.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定