首页 > 代码库 > 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分页的方式及陷阱的演示。如有错误或者不妥之处,欢迎指正。