This following class will help you do it. You can find the explanation of code inline.
public static class MyClass
{
public static IQueryable<T> Order<T>(
IQueryable<T> queryable,
List<string> fields,
//We pass LambdaExpression because the selector property type can be anything
Dictionary<string, LambdaExpression> expressions)
{
//Start with input queryable
IQueryable<T> result = queryable;
//Loop through fields
for (int i = 0; i < fields.Count; i++)
{
bool ascending = fields[i][0] == '+';
string field = fields[i].Substring(1);
LambdaExpression expression = expressions[field];
MethodInfo method = null;
//Based on sort order and field index, determine which method to invoke
if (ascending && i == 0)
method = OrderbyMethod;
else if (ascending && i > 0)
method = ThenByMethod;
else if (!ascending && i == 0)
method = OrderbyDescendingMethod;
else
method = ThenByDescendingMethod;
//Invoke appropriate method
result = InvokeQueryableMethod( method, result, expression);
}
return result;
}
//This method can invoke OrderBy or the other methods without
//getting as input the expression return value type
private static IQueryable<T> InvokeQueryableMethod<T>(
MethodInfo methodinfo,
IQueryable<T> queryable,
LambdaExpression expression)
{
var generic_order_by =
methodinfo.MakeGenericMethod(
typeof(T),
expression.ReturnType);
return (IQueryable<T>)generic_order_by.Invoke(
null,
new object[] { queryable, expression });
}
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 MyClass()
{
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<>) }));
}
}
Here is an example usage:
public class Person
{
public int Age { get; set; }
public string Name { get; set; }
public override string ToString()
{
return Name + ", " + Age;
}
}
class Program
{
static void Main(string[] args)
{
List<Person> persons = new List<Person>
{
new Person {Name = "yacoub", Age = 30},
new Person {Name = "yacoub", Age = 32},
new Person {Name = "adam", Age = 30},
new Person {Name = "adam", Age = 33},
};
var query = MyClass.Order(
persons.AsQueryable(),
new List<string> { "+Name", "-Age" },
new Dictionary<string, LambdaExpression>
{
{"Name", (Expression<Func<Person, string>>) (x => x.Name)},
{"Age", (Expression<Func<Person, int>>) (x => x.Age)}
});
var result = query.ToList();
}
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…