-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathOrderByExtensions.cs
128 lines (104 loc) · 4.38 KB
/
OrderByExtensions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/*
* Written by Ronnie Overby
* and part of the Ronnie Overby Grab Bag: https://github.com/ronnieoverby/RonnieOverbyGrabBag
*/
// Thanks to Marc Gravell for some of the methods (http://stackoverflow.com/a/233505/64334)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace Overby
{
public class SortDescriptor
{
public string Property { get; set; }
public bool Descending { get; set; }
public SortDescriptor()
{
// for serializers
}
public SortDescriptor(string property, bool descending = false)
{
Property = property;
Descending = descending;
}
// parses strings like "Name DESC" or "Whatever ASC"
// ascending is the default
public static SortDescriptor Parse(string s)
{
var parts = s.Split().Where(x => !string.IsNullOrWhiteSpace(x)).ToArray();
var property = parts.FirstOrDefault();
var desc = parts.Skip(1).Take(1).Any(p => p.StartsWith("d", StringComparison.OrdinalIgnoreCase));
return new SortDescriptor(property, desc);
}
}
public static class SortingExtensions
{
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, params string[] sortExpressions)
{
return source.OrderBy(sortExpressions.Select (SortDescriptor.Parse));
}
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, IEnumerable<SortDescriptor> sorts)
{
if (sorts == null) throw new ArgumentNullException("sorts");
IOrderedQueryable<T> sorted = null;
var i = 0;
foreach (var sort in sorts)
{
sorted = i == 0
? source.OrderBy(sort.Property, sort.Descending)
: sorted.ThenBy(sort.Property, sort.Descending);
i++;
}
return sorted ?? source.OrderBy(x => 0);
}
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property, bool desc)
{
return desc ? source.OrderByDescending(property) : source.OrderBy(property);
}
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
{
return ApplyOrder(source, property, "OrderBy");
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
{
return ApplyOrder(source, property, "OrderByDescending");
}
public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property, bool desc)
{
return desc ? source.ThenByDescending(property) : source.ThenBy(property);
}
public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder(source, property, "ThenBy");
}
public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder(source, property, "ThenByDescending");
}
private static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
{
var props = property.Split('.');
var type = typeof(T);
var arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (var prop in props)
{
// use reflection (not ComponentModel) to mirror LINQ
var pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
var delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
var lambda = Expression.Lambda(delegateType, expr, arg);
var result = 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[] { source, lambda });
return (IOrderedQueryable<T>)result;
}
}
}