AppBox升级进行时 - 如何向OrderBy传递字符串参数(Entity Framework)
AppBox 是基于 FineUI 的通用权限管理框架,包括用户管理、职称管理、部门管理、角色管理、角色权限管理等模块。
Entity Framework提供的排序功能
再来回顾一下上篇文章,加载用户列表并进行排序数据库分页的代码:
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 | var q = DB.Users.Include(u => u.Dept); // 在用户名称中搜索 string searchText = ttbSearchMessage.Text.Trim(); if (!String.IsNullOrEmpty(searchText)) { q = q.Where(u => u.Name.Contains(searchText) || u.ChineseName.Contains(searchText) || u.EnglishName.Contains(searchText)); } // 过滤启用状态 if (rblEnableStatus.SelectedValue != "all" ) { q = q.Where(u => u.Enabled == (rblEnableStatus.SelectedValue == "enabled" ? true : false )); } // 在查询添加之后,排序和分页之前获取总记录数 Grid1.RecordCount = q.Count(); // 排列 q = q.OrderBy(u => u.Name); // 数据库分页 q = q.Skip(Grid1.PageIndex * Grid1.PageSize).Take(Grid1.PageSize); Grid1.DataSource = q; Grid1.DataBind(); |
让我们把关注点集中到排序代码上:
1 | q = q.OrderBy(u => u.Name); |
在FineUI实际应用中,我们一般是从表格的 SortField 中读取排序字段,显然EF提供的OrderBy无法接受字符串表示的排序字段。
手工创建Lamba表达式
通过搜索发现了这个帖子:http://stackoverflow.com/questions/10072250/generic-funct-k-to-sort-collections-of-different-types/10074873
据此我们可以写出如下的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public Expression<Func<T, To>> GetSortExpression<T, To>(String sortBy) { var param = Expression.Parameter( typeof (T), "x" ); Expression expr = Expression.Property(param, sortBy); return Expression.Lambda<Func<T, To>>(expr, param); } protected IQueryable<T> Sort<T>(IQueryable<T> q, FineUI.Grid grid) { string sortField = grid.SortField; if (grid.SortDirection == "ASC" ) { q = q.OrderBy(GetSortExpression<T, object >(sortField)); } else { q = q.OrderByDescending(GetSortExpression<T, object >(sortField)); } return q; } |
经过测试,我们发现这个方法不支持bool, int, DateTime, DateTime?类型的列排序。
经过扩展后的代码如下所示:
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 | protected IQueryable<T> Sort<T>(IQueryable<T> q, FineUI.Grid grid) { string sortField = grid.SortField; var propertyType = typeof (T).GetProperty(sortField).PropertyType; if (grid.SortDirection == "ASC" ) { if (propertyType == typeof ( bool )) { q = q.OrderBy(GetSortExpression<T, bool >(sortField)); } else if (propertyType == typeof ( int )) { q = q.OrderBy(GetSortExpression<T, int >(sortField)); } else if (propertyType == typeof (DateTime)) { q = q.OrderBy(GetSortExpression<T, DateTime>(sortField)); } else if (propertyType == typeof (DateTime?)) { q = q.OrderBy(GetSortExpression<T, DateTime?>(sortField)); } else { q = q.OrderBy(GetSortExpression<T, object >(sortField)); } } else { if (propertyType == typeof ( bool )) { q = q.OrderByDescending(GetSortExpression<T, bool >(sortField)); } else if (propertyType == typeof ( int )) { q = q.OrderByDescending(GetSortExpression<T, int >(sortField)); } else if (propertyType == typeof (DateTime)) { q = q.OrderByDescending(GetSortExpression<T, DateTime>(sortField)); } else if (propertyType == typeof (DateTime?)) { q = q.OrderByDescending(GetSortExpression<T, DateTime?>(sortField)); } else { q = q.OrderByDescending(GetSortExpression<T, object >(sortField)); } } return q; } |
但这种做法过于臃肿,有没有更好的办法呢?
更好的SortBy扩展方法
后来,我们发现了这篇文章:http://stackoverflow.com/questions/3945645/sorting-gridview-with-entity-framework
通过对 IQueryable<T> 进行扩展,提供了接受类似 "Name DESC", "CreateTime", "CreateTime DESC" 参数的 SortBy 方法,更具有通用性。
原始的SortBy扩展方法:
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 | public static class QueryExtensions { public static IQueryable<T> SortBy<T>( this IQueryable<T> source, string propertyName) { if (source == null ) { throw new ArgumentNullException( "source" ); } // DataSource control passes the sort parameter with a direction // if the direction is descending int descIndex = propertyName.IndexOf( " DESC" ); if (descIndex >= 0) { propertyName = propertyName.Substring(0, descIndex).Trim(); } if (String.IsNullOrEmpty(propertyName)) { return source; } ParameterExpression parameter = Expression.Parameter(source.ElementType, String.Empty); MemberExpression property = Expression.Property(parameter, propertyName); LambdaExpression lambda = Expression.Lambda(property, parameter); string methodName = (descIndex < 0) ? "OrderBy" : "OrderByDescending" ; Expression methodCallExpression = Expression.Call( typeof (Queryable), methodName, new Type[] { source.ElementType, property.Type }, source.Expression, Expression.Quote(lambda)); return source.Provider.CreateQuery<T>(methodCallExpression); } } |
不过这个方法不支持"Name ASC"形式的参数,所以我们进行了简单的修正,修正后的SortBy扩展方法:
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 | public static class QueryExtensions { public static IQueryable<T> SortBy<T>( this IQueryable<T> source, string sortExpression) { if (source == null ) { throw new ArgumentNullException( "source" ); } string sortDirection = String.Empty; string propertyName = String.Empty; sortExpression = sortExpression.Trim(); int spaceIndex = sortExpression.Trim().IndexOf( " " ); if (spaceIndex < 0) { propertyName = sortExpression; sortDirection = "ASC" ; } else { propertyName = sortExpression.Substring(0, spaceIndex); sortDirection = sortExpression.Substring(spaceIndex + 1).Trim(); } if (String.IsNullOrEmpty(propertyName)) { return source; } ParameterExpression parameter = Expression.Parameter(source.ElementType, String.Empty); MemberExpression property = Expression.Property(parameter, propertyName); LambdaExpression lambda = Expression.Lambda(property, parameter); string methodName = (sortDirection == "ASC" ) ? "OrderBy" : "OrderByDescending" ; Expression methodCallExpression = Expression.Call( typeof (Queryable), methodName, new Type[] { source.ElementType, property.Type }, source.Expression, Expression.Quote(lambda)); return source.Provider.CreateQuery<T>(methodCallExpression); } } |
优化后的排序分页代码
首先在页面基类PageBase中定义排序和分页的代码(使用了前面定义的 SortBy 扩展函数):
1 2 3 4 5 6 7 8 9 | protected IQueryable<T> Sort<T>(IQueryable<T> q, FineUI.Grid grid) { return q.SortBy(grid.SortField + " " + grid.SortDirection); } protected IQueryable<T> SortAndPage<T>(IQueryable<T> q, FineUI.Grid grid) { return Sort(q, grid).Skip(grid.PageIndex * grid.PageSize).Take(grid.PageSize); } |
最终查询用户列表的代码:
var q = DB.Users.Include(u => u.Dept); // 在用户名称中搜索 string searchText = ttbSearchMessage.Text.Trim(); if (!String.IsNullOrEmpty(searchText)) { q = q.Where(u => u.Name.Contains(searchText) || u.ChineseName.Contains(searchText) || u.EnglishName.Contains(searchText)); } // 过滤启用状态 if (rblEnableStatus.SelectedValue != "all" ) { q = q.Where(u => u.Enabled == (rblEnableStatus.SelectedValue == "enabled" ? true : false )); } // 在查询添加之后,排序和分页之前获取总记录数 Grid1.RecordCount = q.Count(); // 排列和数据库分页 q = SortAndPage<User>(q, Grid1); Grid1.DataSource = q; Grid1.DataBind(); |
下载或捐赠AppBox
1. AppBox v2.1 是免费软件,免费提供下载:http://fineui.com/bbs/forum.php?mod=viewthread&tid=3788
2. AppBox v3.0 是捐赠软件,你可以通过捐赠作者来获取AppBox v3.0的全部源代码(http://fineui.com/donate/)。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
2012-09-10 [原创]ExtAspNet秘密花园(十二) — 布局之锚点布局
2009-09-10 http://extasp.net/ 浴火重生
2009-09-10 ExtAspNet应用技巧(十五) - 树形菜单与手风琴式菜单