using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace DotNetCore { public static class ThenByHelper { public static IQueryable ThenBy(this IQueryable collection, string orderBy) { foreach (OrderByInfo orderByInfo in ParseOrderBy(orderBy)) collection = ApplyThenBy(collection, orderByInfo); return collection; } private static IQueryable ApplyThenBy(IQueryable collection, OrderByInfo orderByInfo) { string[] props = orderByInfo.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, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); 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 (orderByInfo.Direction == SortDirection.Ascending) methodName = "ThenBy"; else methodName = "ThenByDescending"; return (IOrderedQueryable)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 }); } private static IEnumerable ParseOrderBy(string orderBy) { if (String.IsNullOrEmpty(orderBy)) yield break; string[] items = orderBy.Split(','); 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 ASC, Property2 DESC", item)); string prop = pair[0].Trim(); if (String.IsNullOrEmpty(prop)) throw new ArgumentException("Invalid Property. Order By Format: Property, Property2 ASC, Property2 DESC"); SortDirection dir = SortDirection.Ascending; if (pair.Length == 2) dir = ("desc".Equals(pair[1].Trim(), StringComparison.OrdinalIgnoreCase) ? SortDirection.Descending : SortDirection.Ascending); yield return new OrderByInfo() { PropertyName = prop, Direction = dir }; } } private class OrderByInfo { public string PropertyName { get; set; } public SortDirection Direction { get; set; } } private enum SortDirection { Ascending = 0, Descending = 1 }; } }