首页 > 代码库 > EF分页中的陷阱
EF分页中的陷阱
(一) 前言
EF使用非常简单,但是如果使用不当就会误入EF陷阱中。下面讲解了几种分页方式的对比,以及各种方式的缺陷。
(二) 陷阱一 Expression<Func<T, TResult>> 和Func<T, TResult>的区别
说明:System.Linq.Expressions.Expression<Func<T, TResult>>和Func<T, TResult>是有很大区别的。Func<T,TResult> 本身就是一个委托(delegate),而Expression<Func<T,TResult>>确实一个表达式,只有在编译之后才 会变成委托,那么在EF中到底使用哪一个呢?又是为什么呢?如果我们写成Func<T,TResult>作为参数传递给 where方法进行Linq查询时,Entity FrameWork将会产生全表查询,将整个数据库表忠的数据加载到内存中,然后再内存中根据where中的条件进一步查询,而 Expression<Func<t,bool>>只是查询出来你where条件中的数据,不会进行全表查询。
我们以查询参数配置表为例。
查询代码如下:
1 private void BindData() 2 { 3 var entitys = bll.GetPagedEntitys(Pager.CurrentPageIndex, Pager.PageSize, out RowCount, out PageCount, c => c.Deleted == false, false, c => c.Id); 4 5 BindRptData(rptList, entitys); 6 Pager.RecordCount = RowCount; 7 }
例一:Func<T, TResult>分页查询,代码如下:
1 public List<T> GetPagedEntitys<S>(int pageIndex, int pageSize, out int rows, out int totalPage, Func<T, bool> whereLambds, bool isAsc,Func<T, S> orderByLambds) 2 { 3 var temp = db.Set<T>().Where<T>(whereLambds); 4 rows = temp.Count(); 5 totalPage = rows % pageSize == 0 ? rows / pageSize : rows / pageSize + 1; 6 temp = isAsc ? temp.OrderBy<T, S>(orderByLambds) : temp.OrderByDescending<T, S>(orderByLambds); 7 temp = temp.Skip<T>(pageSize * (pageIndex - 1)).Take<T>(pageSize); 8 9 return temp.ToList<T>(); 10 }
用 SQL Server Profiler工具分析查询结果如下:
例二:Expression<Func<T, TResult>>分页查询,代码如下:
1 public List<T> GetPagedEntitys<S>(int pageIndex, int pageSize, out int rows, out int totalPage, Expression<Func<T, bool>> whereLambds, bool isAsc, Expression<Func<T, S>> orderByLambds) 2 { 3 var temp = db.Set<T>().Where<T>(whereLambds); 4 rows = temp.Count(); 5 totalPage = rows % pageSize == 0 ? rows / pageSize : rows / pageSize + 1; 6 temp = isAsc ? temp.OrderBy<T, S>(orderByLambds) : temp.OrderByDescending<T, S>(orderByLambds); 7 temp = temp.Skip<T>(pageSize * (pageIndex - 1)).Take<T>(pageSize); 8 9 return temp.ToList<T>(); 10 }
用 SQL Server Profiler工具分析查询结果如下:
(二) 陷阱二 EF SqlQuery方法拼接sql语句的陷阱
说明:EF给开发者提供了SqlQuery方法直接拼接sql语句进行查询,这样既可以让开发者尽情的拼接sql,又可以使用EF提供的ORM将table转换成model。
然而如果使用不当也会造成在内存中分页的悲剧。
我们以查询公告表为例。
查询代码如下:
1 private void BindData() 2 { 3 string where = GetQueryString(true);//自动组装过滤然后拼接sql查询条件--缺点是将业务逻辑放在了UI层(也可以在bll中做处理)。 4 var entitys = bll.GetPagedEntitys(Pager.CurrentPageIndex, Pager.PageSize, out RowCount, out PageCount, where, "Id desc"); 5 6 BindRptData(rptList, entitys); 7 Pager.RecordCount = RowCount; 8 //搜索后显示搜索条件 9 Title_string_like.Value = http://www.mamicode.com/GetQueryString("Title_string_like"); 10 FromWhere_string_like.Value = http://www.mamicode.com/GetQueryString("FromWhere_string_like"); 11 AddTime_string_gt.Value = http://www.mamicode.com/GetQueryString("AddTime_string_gt"); 12 AddTime_string_lt.Value = http://www.mamicode.com/GetQueryString("AddTime_string_lt"); 13 }
关于第9行到第12行的功能可以参考博客:http://www.cnblogs.com/eggTwo/p/3682955.html
例三:SqlQuery方法结合Skip、Take分页查询,代码如下:
1 public List<T> GetPagedEntitys(int pageIndex, int pageSize, out int rows, out int totalPage, string where, string orderKey, params object[] paramss) 2 { 3 string sqls = "select * from " + typeof(T).Name; 4 if (!string.IsNullOrEmpty(where)) 5 { 6 sqls = sqls + " where 1=1 " + where; 7 } 8 if (!string.IsNullOrEmpty(orderKey)) 9 { 10 sqls += " order by " + orderKey; 11 } 12 var temp = db.Database.SqlQuery<T>(sqls, paramss); 13 rows = temp.Count(); 14 if (rows % pageSize == 0) 15 { 16 totalPage = rows / pageSize; 17 } 18 else 19 { 20 totalPage = rows / pageSize + 1; 21 } 22 23 temp = temp.Skip(pageSize * (pageIndex - 1)).Take(pageSize); 24 return temp.ToList<T>(); 25 }
用 SQL Server Profiler工具分析查询结果如下:
例四:SqlQuery方法row_number()函数自定义分页查询,代码如下:
1 public List<T> GetPagedEntitys(int pageIndex, int pageSize, out int rows, out int totalPage, string where, string orderKey, params object[] paramss) 2 { 3 4 string sqls = ""; 5 string tableName = typeof(T).Name;//获取表名 6 string sql = string.Format("select *, row_number() over (order by {0} ) as row_number from {1}", string.IsNullOrEmpty(orderKey) ? "Id" : orderKey, tableName); 7 string where1 = !string.IsNullOrEmpty(where) ? " where 1=1 " + where : ""; 8 int tag = (pageIndex - 1) * pageSize; 9 sqls = string.Format(@"select top ({0}) * from 10 ( 11 {1} 12 {2} 13 ) as t 14 where t.row_number > {3}", pageSize, sql, where1, tag); 15 //获取数据 16 var list = db.Database.SqlQuery<T>(sqls, paramss).ToList<T>(); 17 18 //通过自定义的class R 取得总页码数和记录数 19 string sqlCount = string.Format("select count(1) as Rows from {0} {1}", tableName, where1); 20 rows = db.Database.SqlQuery<R>(sqlCount, paramss).ToList()[0].Rows; 21 totalPage = rows % pageSize == 0 ? rows / pageSize : rows / pageSize + 1; 22 23 return list; 24 25 } 26 public class R 27 { 28 public int Rows { get; set; } 29 }
说明:上述分页方法也是我全面改造EF分页查询的一个典范。从上述代码可以看出,这完全是自定义的拼接sql的分页查询,也就是说我们完全掌握了自主权。只是利用EF的ORM功能将table转换成model,其它的都是自己实现的。大家可能看到上述代码定义了一个类R,这是因为我们要查询总共的记录数。
用 SQL Server Profiler工具分析查询结果如下:
(三) 小结
以上叙述,就完成了几种EF分页的方式及陷阱的演示。如有错误或者不妥之处,欢迎指正。