• 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 = GetQueryString("Title_string_like");
    10         FromWhere_string_like.Value = GetQueryString("FromWhere_string_like");
    11         AddTime_string_gt.Value = GetQueryString("AddTime_string_gt");
    12         AddTime_string_lt.Value = 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分页的方式及陷阱的演示。如有错误或者不妥之处,欢迎指正。

  • 相关阅读:
    sklearn之集成算法模型
    ScrollView嵌套ListView,GridView数据加载不全问题的解决
    ProgressBar---进度条
    Android中Button的五种监听事件
    ListView加载性能优化---ViewHolder---分页
    Menu与ActionBar的爱恨情仇
    toast组件小结
    数据挖掘导论---揭开数据挖掘神秘面纱-1
    腾讯地图坐标转百度地图坐标
    微信录音接口的调用以及amr文件转码MP3文件的实现
  • 原文地址:https://www.cnblogs.com/eggTwo/p/3763224.html
Copyright © 2020-2023  润新知