OrderBy based on list of fields and Asc / Desc rules

前端 未结 3 1423
春和景丽
春和景丽 2020-12-10 20:37

I have the following List with OrderBy parameters:

List fields = new List { \"+created\", \"-approved\"         


        
3条回答
  •  暗喜
    暗喜 (楼主)
    2020-12-10 21:17

    This answer is the joint effort of @YacoubMassad and me. Take a look at the separate answers for details. The following code works perfectly and even translates to SQL without issues (I checked the query with the answer to this question on a 2008 R2), so all the sorting is done on the server (or wherever you data is, it works with simple lists too of course).

    Example usage:

    OrderExpression expression = new OrderExpression()
        .Add(x => x.Created, "created")
        .Add(x => x.Approved, "approved")
        .Add(x => x.Author.Name, "author");
    
    IQueryable posts = _context.posts.AsQueryable();
    
    posts = posts.OrderBy(expression, "+created", "-approved", "+author");
    // OR
    posts = posts.OrderBy(expression, new string[]{"+created", "-approved", "+author"});
    // OR
    posts = posts.OrderBy(expression, fields.ToArray[]);
    

    And of course a live demo on dotNetFiddle

    Code:

    public class OrderExpressions
    {
        private readonly Dictionary _mappings = 
            new Dictionary();
    
        public OrderExpressions Add(Expression> expression, string keyword)
        {
            _mappings.Add(keyword, expression);
            return this;
        }
    
        public LambdaExpression this[string keyword]
        {
            get { return _mappings[keyword]; }
        }
    }
    
    public static class KeywordSearchExtender
    {
        private static readonly MethodInfo OrderbyMethod;
        private static readonly MethodInfo OrderbyDescendingMethod;
    
        private static readonly MethodInfo ThenByMethod;
        private static readonly MethodInfo ThenByDescendingMethod;
    
        //Here we use reflection to get references to the open generic methods for
        //the 4 Queryable methods that we need
        static KeywordSearchExtender()
        {
            OrderbyMethod = typeof(Queryable)
                .GetMethods()
                .First(x => x.Name == "OrderBy" &&
                    x.GetParameters()
                        .Select(y => y.ParameterType.GetGenericTypeDefinition())
                        .SequenceEqual(new[] { typeof(IQueryable<>), typeof(Expression<>) }));
    
            OrderbyDescendingMethod = typeof(Queryable)
                .GetMethods()
                .First(x => x.Name == "OrderByDescending" &&
                    x.GetParameters()
                        .Select(y => y.ParameterType.GetGenericTypeDefinition())
                        .SequenceEqual(new[] { typeof(IQueryable<>), typeof(Expression<>) }));
    
            ThenByMethod = typeof(Queryable)
                .GetMethods()
                .First(x => x.Name == "ThenBy" &&
                    x.GetParameters()
                        .Select(y => y.ParameterType.GetGenericTypeDefinition())
                        .SequenceEqual(new[] { typeof(IOrderedQueryable<>), typeof(Expression<>) }));
    
            ThenByDescendingMethod = typeof(Queryable)
                .GetMethods()
                .First(x => x.Name == "ThenByDescending" &&
                    x.GetParameters()
                        .Select(y => y.ParameterType.GetGenericTypeDefinition())
                        .SequenceEqual(new[] { typeof(IOrderedQueryable<>), typeof(Expression<>) }));
        }
    
        //This method can invoke OrderBy or the other methods without
        //getting as input the expression return value type
        private static IQueryable InvokeQueryableMethod(
            MethodInfo methodinfo,
            IQueryable queryable,
            LambdaExpression expression)
        {
            var generic_order_by =
                methodinfo.MakeGenericMethod(
                    typeof(T),
                    expression.ReturnType);
    
            return (IQueryable)generic_order_by.Invoke(
                null,
                new object[] { queryable, expression });
        }
    
        public static IQueryable OrderBy(this IQueryable data, 
            OrderExpressions mapper, params string[] arguments)
        {
            if (arguments.Length == 0)
                throw new ArgumentException(@"You need at least one argument!", "arguments");
    
            List sorting = arguments.Select(a => new SortArgument(a)).ToList();
    
            IQueryable result = null;
    
            for (int i = 0; i < sorting.Count; i++)
            {
                SortArgument sort = sorting[i];
                LambdaExpression lambda = mapper[sort.Keyword];
    
                if (i == 0)
                    result = InvokeQueryableMethod(sort.Ascending ? 
                        OrderbyMethod : OrderbyDescendingMethod, data, lambda);
                else
                    result = InvokeQueryableMethod(sort.Ascending ? 
                        ThenByMethod : ThenByDescendingMethod, result, lambda);
            }
    
            return result;
        }
    }
    
    public class SortArgument
    {
        public SortArgument()
        { }
    
        public SortArgument(string term)
        {
            if (term.StartsWith("-"))
            {
                Ascending = false;
                Keyword = term.Substring(1);
            }
            else if (term.StartsWith("+"))
            {
                Ascending = true;
                Keyword = term.Substring(1);
            }
            else
            {
                Ascending = true;
                Keyword = term;
            }
        }
    
        public string Keyword { get; set; }
        public bool Ascending { get; set; }
    }
    

提交回复
热议问题