处理 GridView.OnSorting() 并使用 LINQ 动态创建排序表达式






4.90/5 (16投票s)
如何使用 LINQ 表达式树从 GridViewSortEventArgs 创建排序表达式。
引言
ASP.NET 2.0 中的 GridView
控制是一个很棒且广泛使用的控件
<asp:GridView runat="server" ID="gridPersons"
AutoGenerateColumns="false" AllowSorting="true"
OnSorting="gridPersons_Sorting">
<Columns>
<asp:BoundField HeaderText="First name"
DataField="FirstName" SortExpression="FirstName" />
<asp:BoundField HeaderText="Last name"
DataField="LastName" SortExpression="LastName" />
</Columns>
</asp:GridView>
protected void gridPersons_Sorting(object sender, GridViewSortEventArgs e)
{
string sortExpression = e.SortExpression;
}
但有一个显著的限制:排序事件参数 GridViewSortEventArg
基于标记中的字符串值。因此,它不能直接用于使用 LINQ 进行编程排序。
本文档描述了如何将字符串参数转换为排序表达式。
背景
假设我们有一个强类型类的序列,例如 Person
,并且我们想将其绑定到 GridView
,然后通过单击相应的列对其进行排序
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
IEnumerable<Person> persons = GetPersons();
gridPersons.DataSource = persons.ToArray();
gridPersons.DataBind();
通常,我们可以硬编码排序表达式(即,要排序的属性)和方向(升序、降序)。
protected void gridPersons_Sorting(object sender, GridViewSortEventArgs e)
{
Func<Person, object> prop = null;
switch (e.SortExpression)
{
case "FirstName":
{
prop = p => p.FirstName;
break;
}
case "LastName":
{
prop = p => p.LastName;
break;
}
}
Func<IEnumerable<Person>, Func<Person, object>, IEnumerable<Person>> func = null;
switch (e.SortDirection)
{
case SortDirection.Ascending:
{
func = (c, p) => c.OrderBy(p);
break;
}
case SortDirection.Descending:
{
func = (c, p) => c.OrderByDescending(p);
break;
}
}
IEnumerable<Person> persons = GetPersons();
persons = func(persons, prop);
gridPersons.DataSource = persons.ToArray();
gridPersons.DataBind();
}
但这种方法存在一些缺点:所有对象的属性以及排序方向都被硬编码。更好的方法是动态创建所有这些。
解决方案
让我们将逻辑封装到一些专门的类中。
第一个将 SortDirection
转换为 Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>>
,这是一个接受序列和另一个接受对象并返回其属性之一的函数;并返回按该对象属性排序的序列。
public static class SortExpressionConverter<T>
{
private static IDictionary<SortDirection, ISortExpression> expressions =
new Dictionary<SortDirection, ISortExpression>
{
{ SortDirection.Ascending, new OrderByAscendingSortExpression() },
{ SortDirection.Descending, new OrderByDescendingSortExpression() }
};
interface ISortExpression
{
Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>> GetExpression();
}
class OrderByAscendingSortExpression : ISortExpression
{
public Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>> GetExpression()
{
return (c, f) => c.OrderBy(f);
}
}
class OrderByDescendingSortExpression : ISortExpression
{
public Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>> GetExpression()
{
return (c, f) => c.OrderByDescending(f);
}
}
public static Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>>
Convert(SortDirection direction)
{
ISortExpression sortExpression = expressions[direction];
return sortExpression.GetExpression();
}
}
第二个构建一个 lambda 表达式 Expression<Func<T, object>>
并返回基础 lambda Func<T, object>
public static class SortLambdaBuilder<T>
{
public static Func<T, object> Build(string columnName, SortDirection direction)
{
// x
ParameterExpression param = Expression.Parameter(typeof(T), "x");
// x.ColumnName1.ColumnName2
Expression property = columnName.Split('.')
.Aggregate<string, Expression>
(param, (c, m) => Expression.Property(c, m));
// x => x.ColumnName1.ColumnName2
Expression<Func<T, object>> lambda = Expression.Lambda<Func<T, object>>(
Expression.Convert(property, typeof(object)),
param);
Func<T, object> func = lambda.Compile();
return func;
}
}
将这些类的用法包装到扩展方法中
public static class CollectionExtensions
{
public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> collection,
string columnName, SortDirection direction)
{
Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>> expression =
SortExpressionConverter<T>.Convert(direction);
Func<T, object> lambda =
SortLambdaBuilder<T>.Build(columnName, direction);
IEnumerable<T> sorted = expression(collection, lambda);
return sorted;
}
}
所以现在我们有一个灵活、完全动态且通用的解决方案
protected void gridPersons_Sorting(object sender, GridViewSortEventArgs e)
{
IEnumerable<Person> persons = GetPersons();
persons = persons.OrderBy(e.SortExpression, e.SortDirection);
gridPersons.DataSource = persons.ToArray();
gridPersons.DataBind();
}
兴趣点
以下是一些与该主题相关的 StackOverflow 讨论
历史记录
- 2011 年 8 月 27 日 - 初始发布
- 2012 年 3 月 22 日 - 扩展了
OrderBy<T>()
扩展方法,使其更易于阅读和理解 - 2014 年 7 月 17 日 -
SortLambdaBuilder<T>
支持嵌套属性