using System.Collections.Generic;
using System.Linq.Expressions;
public static void Main()
IQueryable<DemoClass> data = GetTestData().AsQueryable();
OrderExpressions<DemoClass> expressions = new OrderExpressions<DemoClass>()
.Add(x => x.Created, "created")
.Add(x => x.Approved, "approved")
.Add(x =>x.Author.Name, "author");
BuildAndPrintQuery<DemoClass>(data, expressions, "+created", "-approved", "+author");
BuildAndPrintQuery<DemoClass>(data, expressions, "+author", "-created", "-approved");
BuildAndPrintQuery<DemoClass>(data, expressions, "-author", "+created", "-approved");
BuildAndPrintQuery<DemoClass>(data, expressions, "-created");
BuildAndPrintQuery<DemoClass>(data, expressions, "+created");
BuildAndPrintQuery<DemoClass>(data, expressions, "created");
public static void BuildAndPrintQuery<T>(IQueryable<T> data, OrderExpressions<T> expressions, params string[] arguments)
IQueryable<T> result = data.OrderBy(expressions, arguments);
Console.WriteLine(String.Join(", ", arguments));
foreach(T entry in result)
Console.WriteLine(entry);
public static IEnumerable<DemoClass> GetTestData()
yield return new DemoClass
Author = new Person { Name = "Name A" },
Created = DateTime.Now.AddDays(-1)
yield return new DemoClass
Author = new Person { Name = "Name C" },
Created = DateTime.Now.AddDays(-2)
yield return new DemoClass
Author = new Person { Name = "Name E" },
Created = DateTime.Now.AddDays(-3)
yield return new DemoClass
Author = new Person { Name = "Name B" },
Created = DateTime.Now.AddDays(-4)
yield return new DemoClass
Author = new Person { Name = "Name D" },
Created = DateTime.Now.AddDays(-5)
yield return new DemoClass
Author = new Person { Name = "Name A" },
Created = DateTime.Now.AddDays(-11)
yield return new DemoClass
Author = new Person { Name = "Name C" },
Created = DateTime.Now.AddDays(-3)
yield return new DemoClass
Author = new Person { Name = "Name E" },
Created = DateTime.Now.AddDays(-5)
yield return new DemoClass
Author = new Person { Name = "Name B" },
Created = DateTime.Now.AddDays(-8)
yield return new DemoClass
Author = new Person { Name = "Name D" },
Created = DateTime.Now.AddDays(-5)
public DateTime Created { get; set; }
public bool Approved { get; set; }
public Person Author { get; set; }
public override string ToString()
return Created + " " + Approved + " " + Author.Name;
public string Name { get; set; }
public class OrderExpressions<T>
private readonly Dictionary<string, LambdaExpression> _mappings = new Dictionary<string, LambdaExpression>();
public OrderExpressions<T> Add<TKey>(Expression<Func<T, TKey>> expression, string keyword)
_mappings.Add(keyword, expression);
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;
static KeywordSearchExtender()
OrderbyMethod = typeof(Queryable)
.First(x => x.Name == "OrderBy" &&
.Select(y => y.ParameterType.GetGenericTypeDefinition())
.SequenceEqual(new[] { typeof(IQueryable<>), typeof(Expression<>) }));
OrderbyDescendingMethod = typeof(Queryable)
.First(x => x.Name == "OrderByDescending" &&
.Select(y => y.ParameterType.GetGenericTypeDefinition())
.SequenceEqual(new[] { typeof(IQueryable<>), typeof(Expression<>) }));
ThenByMethod = typeof(Queryable)
.First(x => x.Name == "ThenBy" &&
.Select(y => y.ParameterType.GetGenericTypeDefinition())
.SequenceEqual(new[] { typeof(IOrderedQueryable<>), typeof(Expression<>) }));
ThenByDescendingMethod = typeof(Queryable)
.First(x => x.Name == "ThenByDescending" &&
.Select(y => y.ParameterType.GetGenericTypeDefinition())
.SequenceEqual(new[] { typeof(IOrderedQueryable<>), typeof(Expression<>) }));
private static IQueryable<T> InvokeQueryableMethod<T>(
LambdaExpression expression)
methodinfo.MakeGenericMethod(
return (IQueryable<T>)generic_order_by.Invoke(
new object[] { queryable, expression });
public static IQueryable<T> OrderBy<T>(this IQueryable<T> data, OrderExpressions<T> mapper, params string[] arguments)
if (arguments.Length == 0)
throw new ArgumentException(@"You need at least one argument!", "arguments");
List<SortArgument> sorting = arguments.Select(a => new SortArgument(a)).ToList();
IQueryable<T> result = null;
for (int i = 0; i < sorting.Count; i++)
SortArgument sort = sorting[i];
LambdaExpression lambda = mapper[sort.Keyword];
result = InvokeQueryableMethod(sorting[i].Ascending ? OrderbyMethod : OrderbyDescendingMethod, data, lambda);
result = InvokeQueryableMethod(sorting[i].Ascending ? ThenByMethod : ThenByDescendingMethod, result, lambda);
public class SortArgument
public SortArgument(string term)
if (term.StartsWith("-"))
Keyword = term.Substring(1);
else if (term.StartsWith("+"))
Keyword = term.Substring(1);
public string Keyword { get; set; }
public bool Ascending { get; set; }