Click here to Skip to main content
15,886,199 members
Articles / General Programming / Sorting
Tip/Trick

Build Dynamic Order by Clause in LINQ Query in C#

Rate me:
Please Sign up or sign in to vote.
4.00/5 (3 votes)
5 Jun 2022CPOL 14.1K   5   1
An overview of a helper class to build dynamic order by clause in LINQ query in C#
This article discusses HelperClass which is used to build OrderBy extension like dynamic SQL.

Introduction

Many times, we need to handle SQL-like OrderBy in C#. This will help you to solve dynamic sorting. This is an OrderBy extension which takes a SQL-Like OrderBy string and sort a IQueryable or IEnumerable collection.

Background

Good to have some information on:

  • yield
  • IQueryable
  • IEnumerable
  • Reflection

Using the Code

To use this extension, we just need to pass list and the sorting parameter in which you want to sort a list. Like:

C#
SortingHelper.SortByClause(list, SortingParms).ToList();
C#
public static class SortingHelper
    {
        private class SortByInfo
        {
            public SortDirection Direction { get; set; }
            public string PropertyName { get; set; }            
            public bool Initial { get; set; }
        }

        private enum SortDirection
        {
            Ascending = 0,
            Descending = 1
        }
        
        public static IEnumerable<T> SortByClause<T>
               (this IEnumerable<T> enumerable, string sortBy)
        {
            return enumerable.AsQueryable().SortBy(sortBy).AsEnumerable();
        }

        public static IQueryable<T> SortBy<T>
               (this IQueryable<T> collection, string sortBy)
        {
            foreach(SortByInfo sortByInfo in ParseOrderBy(sortBy))
                collection = ApplyOrderBy<T>(collection, sortByInfo);

            return collection;
        }
        
        private static IEnumerable<SortByInfo> ParseOrderBy(string sortBy)
        {
            if (String.IsNullOrEmpty(sortBy))
                yield break;

            string[] items = sortBy.Split(',');
            bool initial = true;
            foreach(string item in items)
            {
                string[] pair = item.Trim().Split('-');

                if (pair.Length > 2)
                    throw new ArgumentException(String.Format
                    ("Invalid OrderBy string '{0}'. Order By Format: Property, 
                    Property2-DESC, Property2-ASC",item));

                string prop = pair[0].Trim();

                if(String.IsNullOrEmpty(prop))
                    throw new ArgumentException("Invalid Property. 
                    Order By Format: Property, Property2-DESC, Property2-ASC");
                
                SortDirection dir = SortDirection.Ascending;
                
                if (pair.Length == 2)
                    dir = ("desc".Equals(pair[1].Trim(), 
                          StringComparison.OrdinalIgnoreCase) ? 
                          SortDirection.Descending : SortDirection.Ascending);

                yield return new SortByInfo() { PropertyName = prop, 
                             Direction = dir, Initial = initial };

                initial = false;
            }
        }       

        private static IQueryable<T> ApplyOrderBy<T>
                (IQueryable<T> collection, SortByInfo sortByInfo)
        {
            string[] props = sortByInfo.PropertyName.Split('.');
            Type type = typeof(T);

            ParameterExpression arg = Expression.Parameter(type, "x");
            Expression expr = arg;
            foreach (string prop in props)
            {
                PropertyInfo pi = type.GetProperty(prop);
                expr = Expression.Property(expr, pi);
                type = pi.PropertyType;
            }
            Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
            LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
            string methodName = String.Empty;

            if (!sortByInfo.Initial && collection is IOrderedQueryable<T>)
            {
                if (sortByInfo.Direction == SortDirection.Ascending)
                    methodName = "ThenBy";
                else
                    methodName = "ThenByDescending";
            }
            else
            {
                if (sortByInfo.Direction == SortDirection.Ascending)
                     methodName = "OrderBy";
                else
                     methodName = "OrderByDescending";
            }

            return (IOrderedQueryable<T>)typeof(Queryable).GetMethods().Single(
                method => method.Name == methodName
                        && method.IsGenericMethodDefinition
                        && method.GetGenericArguments().Length == 2
                        && method.GetParameters().Length == 2)
                .MakeGenericMethod(typeof(T), type)
                .Invoke(null, new object[] { collection, lambda });
        }        
    }

Hope this will help!

History

  • 5th June, 2022: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
India India
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionI wrote something similar Pin
SaiRictus5-Jun-22 8:52
SaiRictus5-Jun-22 8:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.