Created
December 15, 2017 17:49
-
-
Save brandanmajeske/8d30bff45ad7d3ab63d66e65a8ae2522 to your computer and use it in GitHub Desktop.
Dynamic SQL-like LINQ OrderBy Extention (original author Adam Anderson)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class OrderByHelper | |
{ | |
public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> enumerable, string orderBy) | |
{ | |
return enumerable.AsQueryable().OrderBy(orderBy).AsEnumerable(); | |
} | |
public static IQueryable<T> OrderBy<T>(this IQueryable<T> collection, string orderBy) | |
{ | |
foreach(OrderByInfo orderByInfo in ParseOrderBy(orderBy)) | |
collection = ApplyOrderBy<T>(collection, orderByInfo); | |
return collection; | |
} | |
private static IQueryable<T> ApplyOrderBy<T>(IQueryable<T> 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) | |
{ | |
// use reflection (not ComponentModel) to mirror LINQ | |
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 (!orderByInfo.Initial && collection is IOrderedQueryable<T>) | |
{ | |
if (orderByInfo.Direction == SortDirection.Ascending) | |
methodName = "ThenBy"; | |
else | |
methodName = "ThenByDescending"; | |
} | |
else | |
{ | |
if (orderByInfo.Direction == SortDirection.Ascending) | |
methodName = "OrderBy"; | |
else | |
methodName = "OrderByDescending"; | |
} | |
//TODO: apply caching to the generic methodsinfos? | |
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 }); | |
} | |
private static IEnumerable<OrderByInfo> ParseOrderBy(string orderBy) | |
{ | |
if (String.IsNullOrEmpty(orderBy)) | |
yield break; | |
string[] items = orderBy.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 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, Initial = initial }; | |
initial = false; | |
} | |
} | |
private class OrderByInfo | |
{ | |
public string PropertyName { get; set; } | |
public SortDirection Direction { get; set; } | |
public bool Initial { get; set; } | |
} | |
private enum SortDirection | |
{ | |
Ascending = 0, | |
Descending = 1 | |
} | |
} | |
// Example | |
// list.OrderBy("SomeProperty"); | |
// list.OrderBy("SomeProperty DESC"); | |
// list.OrderBy("SomeProperty DESC, SomeOtherProperty"); | |
// list.OrderBy("SomeSubObject.SomeProperty ASC, SomeOtherProperty DESC"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment