前言:

Dapper是一款非常方便的轻量级的ORM工具,
这里放一个它的文档:
Dapper帮助文档
它拓展了IConnection接口的方法,使其能够查询Model的List,不需要提前的Mapper设置,也支持多种写入参数的方式
与其他ORM框架相比,确实很方便.
但在实际的使用中,当数据量很大时,它的执行效率比原生的ADO.NET低了很多倍.

问题的发现

有需求要把大量的数据查出然后导入ExceL中,
开始是使用的Dapper进行List查询,大概在执行上花了2分钟左右(只是需要的数据的一部分)
后来改做DataTable查询,执行的时间并没有快多少,
最后换了原生的ADO.NET的方式进行查询,
在时间上直接进入10秒以内

这种差距感到很惊讶,故准备进Dapper的源码进行调试
顺便测试一下

測試

在三行不同的查询上打上断点

List<POCO> selList = _SqlDapper.QueryList<POCO>(SQL,null);//这个QueryList是自行封装的,故不显示其内容DataTable Test = _SqlDapper.QueryDataTable(SQL,null);DataTable dt = SQLCommand.ExecuteDataTable(SQL);

(在工具---->设置—>Debuger里把Just My Code给关闭,不然Debug不会进去,至于它会要求链接或下载源码可以不管,点击反编译选项即可)

1.第一条语句进入的Dapper方法:

单步1,进入数据库的连接配置

public ProfiledDbConnection(DbConnection connection, IDbProfiler profiler)
{_connection = (connection ?? throw new ArgumentNullException("connection"));_connection.StateChange += StateChangeHandler;if (profiler != null){_profiler = profiler;}
}

此处源码进行了数据库的连接配置,和查询关联不大,
单步2,查询前的参数配置等等…

public static IEnumerable<T> Query<T>(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
{CommandDefinition command = new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None);//配置SQL语句,参数,事务,超时时间等等IEnumerable<T> data = cnn.QueryImpl<T>(command, typeof(T));  //真正的查询在这里if (!command.Buffered){return data;}return data.ToList();
}

这一步是主要配置参数

单步3,执行SQL进行查询,及类型转化

private static IEnumerable<T> QueryImpl<T>(this IDbConnection cnn, CommandDefinition command, Type effectiveType)
{//获取参数object param = command.Parameters;//验证Sql和参数等的信息Identity identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType());//设置缓存CacheInfo info = GetCacheInfo(identity, param, command.AddToCache);IDbCommand cmd = null;IDataReader reader = null; //Attention Here 它是使用DataReader来进行数据的读取//这里就是问题的所在bool wasClosed = cnn.State == ConnectionState.Closed;try{cmd = command.SetupCommand(cnn, info.ParamReader);if (wasClosed){cnn.Open();}//在这里执行了SQL语句,这个方法只是把查询简单的包装一下,我把它放到下面reader = ExecuteReaderWithFlagsFallback(cmd, wasClosed, CommandBehavior.SingleResult | CommandBehavior.SequentialAccess);(  //这就是那个方法,它不在这个位置,我为了方便把它放到里面来private static IDataReader ExecuteReaderWithFlagsFallback(IDbCommand cmd, bool wasClosed, CommandBehavior behavior){try{   //执行SQL,        return cmd.ExecuteReader(GetBehavior(wasClosed, behavior));}catch (ArgumentException ex){if (Settings.DisableCommandBehaviorOptimizations(behavior, ex)){//执行SQLreturn cmd.ExecuteReader(GetBehavior(wasClosed, behavior));}throw;}})wasClosed = false;DeserializerState tuple = info.Deserializer; //设置反序列化器int hash = GetColumnHash(reader);if (tuple.Func != null && tuple.Hash == hash){goto IL_0174;     //这个地方应该是反编译的问题,没有正常显示(无关紧要)}if (reader.FieldCount != 0){//获取反序列化状态DeserializerState deserializerState2 = info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, 0, -1, returnNullIfFirstMissing: false));tuple = deserializerState2;if (command.AddToCache){    //设置重庆讯缓存SetQueryCache(identity, info);}goto IL_0174;}goto end_IL_00a0;IL_0174://设置反序列化的方法委托Func<IDataReader, object> func = tuple.Func;Type convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;while (reader.Read())  //直接去遍历了整个DataReader{object val = func(reader);   //读取'一行'数据,利用方法委托转化为Object对象if (val == null || val is T){yield return (T)val;   //再对Obj进行强转}else{//再对Obj进行强转yield return (T)Convert.ChangeType(val, convertToType, CultureInfo.InvariantCulture);}}while (reader.NextResult())  //检查DataReader是否处理完{}reader.Dispose();reader = null;command.OnCompleted();end_IL_00a0:;}finally      //关闭连接操作{if (reader != null)  {if (!reader.IsClosed){try{cmd.Cancel();}catch{}}reader.Dispose();}if (wasClosed){cnn.Close();}cmd?.Dispose();}
}

2.第二条语句进入的Dapper方法:

由于Dapper没有返回DataTable的方法,这条语句实际上是把ExecuteReader的dataReader结果转型的DataTable,故效率和上一种的方法差别应该不大,单页需要进入查看一下步骤
单步1:还是用DataReader执行的

public static IDataReader ExecuteReader(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
{CommandDefinition command = new CommandDefinition(sql, param, transaction, commandTimeout, commandType);IDbCommand dbcmd;IDataReader reader = ExecuteReaderImpl(cnn, ref command, CommandBehavior.Default, out dbcmd);return WrappedReader.Create(dbcmd, reader);
}

单步进入执行方法:

private static IDataReader ExecuteReaderImpl(IDbConnection cnn, ref CommandDefinition command, CommandBehavior commandBehavior, out IDbCommand cmd)
{   //因为返回的是DataReader,不需要去处理类型信息,所以不需要(正反)序列化,类型检查,强制转型...//因此效率比上一个方法要好一点Action<IDbCommand, object> paramReader = GetParameterReader(cnn, ref command);cmd = null;bool wasClosed = cnn.State == ConnectionState.Closed;bool disposeCommand = true;try{cmd = command.SetupCommand(cnn, paramReader);if (wasClosed){cnn.Open();}IDataReader reader = ExecuteReaderWithFlagsFallback(cmd, wasClosed, commandBehavior);wasClosed = false;disposeCommand = false;return reader;}finally{if (wasClosed){cnn.Close();}if (cmd != null && disposeCommand){cmd.Dispose();}}
}

3.第三条语句进入的方法:

第三条语句是微软自己的工具,VS当然反编译不了,但它很简单,
只是用原生的DataAdapter.Fill(DataTable table)

SqlDataAdapter adapter = new SqlDataAdapter();  //大概就是使用DataAdapter.F
adapter.SelectCommand = cmd;
adapter.Fill(DataTable);

结果:

执行的时间对比(数据量10w+)
3>2>1
时间大致为
7s : 40s : 50s+

原因

可以很明显的看出Dapper单单使用DataReader,而原生的ADO.NET中用的是DataAdapter
原因归根结底还是DataAdapter和DataReader的适用性问题

根据查到的信息,DataAdapter.Fill()是短连接,一次加载所有数据到DataTable中,
而DataReader是长连接,一行一行的读取数据库查询信息,(通常在一个while循环里读取,这点像JDBC的ResultSet),这样就很容易进行ORM操作,好做类型的转化,加之Dapper的参数设置,类型检查等等,反序列化操作等等…导致了大量数据查询时的效率低下.

这里给一篇作参考的文章
https://blog.csdn.net/u012927285/article/details/44095195

Dapper的效率问题的总结相关推荐

  1. bltoolkit mysql_.NET 轻量级 ORM 框架 - Dapper 介绍

    转自:https://blog.csdn.net/hanjun0612/article/details/52170204 Dapper简单介绍: Dapper is a single file you ...

  2. 关于Dapper.NET的相关论述

    年少时,为何不为自己的梦想去拼搏一次呢?纵使头破血流,也不悔有那年少轻狂.感慨很多,最近事情也很多,博客也很少更新了,毕竟每个人都需要为自己的生活去努力. 最近在一个群里遇到一个人说的话,在这里不再赘 ...

  3. OSS.Core基于Dapper封装(表达式解析+Emit)仓储层的构思及实现

    最近趁着不忙,在构思一个搭建一个开源的完整项目,至于原因以及整个项目框架后边文章我再说明.既然要起一个完整的项目,那么数据仓储访问就必不可少,这篇文章我主要介绍这个新项目(OSS.Core)中我对仓储 ...

  4. .net core 基于Dapper 的分库分表开源框架(core-data)

    一.前言 感觉很久没写文章了,最近也比较忙,写的相对比较少,抽空分享基于Dapper 的分库分表开源框架core-data的强大功能,更好的提高开发过程中的效率:在数据库的数据日积月累的积累下,业务数 ...

  5. 同时支持EF+Dapper的混合仓储,助你快速搭建数据访问层

    背景 17年开始,公司开始向DotNet Core转型,面对ORM工具的选型,当时围绕Dapper和EF发生了激烈的讨论.项目团队更加关注快速交付,他们主张使用EF这种能快速开发的ORM工具:而在线业 ...

  6. 深入Dapper.NET源码

    经过业界前辈.StackOverflow多年推广,「Dapper搭配Entity Framework」成为一种功能强大的组合,它满足「安全.方便.高效.好维护」需求. 但目前中文网路文章,虽然有很多关 ...

  7. 开源Dapper的Lambda扩展-Sikiro.Dapper.Extension V2.0

    去年我在业余时间,自己整了一套dapper的lambda表达式的封装,原本是作为了一个个人的娱乐项目,当时也只支持了Sql Server数据库.随之开源后,有不少朋友也对此做了试用,也对我这个项目提出 ...

  8. PocoClassGenerator:RDBMS所有表/视图生成Dapper POCO类代码

    目录 介绍 开始 第一 第二 演示图片 域逻辑 域代码 下载Github链接 介绍 制作PocoClassGenerator的原因是我经常需要" 生成大量RDBMS的表/视图到dapper ...

  9. 02-大鸭梨博客系统数据库设计及Dapper的使用

    毫无疑问,数据库的设计在一个系统中起了至关重要的作用.我们都知道,系统设计分为两部分,或者说是两个阶段,即数据库设计和功能设计.构建一个完善的系统需要这两个阶段的充分考量.周密设计.合理联接以及密切配 ...

最新文章

  1. Tarjan无向图连通性
  2. android远程控制灯光,智能灯具如何实现远程控制技术
  3. python按时间排序 文件
  4. Win64 驱动内核编程-17. MINIFILTER(文件保护)
  5. uva 10716——Evil Straw Warts Live
  6. 1381. 设计一个支持增量操作的栈
  7. 步步惊芯 软核处理器的内部设计分析 pdf_【深度分析】互联网巨头造“芯”现状...
  8. el 能否定义作用域变量_Python 基础知识全篇-命名空间和作用域
  9. 解决:error: Microsoft Visual C++ 14.0 or greater is required. Get it with “Microsoft C++ Build Tools“
  10. 自作孽!知网终于被查了,涉嫌垄断,背后到底是何方神圣?
  11. 解决VM虚拟机启动后假死
  12. 【转载】浅的不能再浅的浅谈百利金单双胡及全字尖
  13. 容器云的双活与灾备技术
  14. 淘宝手机端详情页的设置
  15. 十大实用网站推荐(1)
  16. 抖音用计算机怎么表白,抖音表白代码
  17. python在园林中的应用_浅析亭在园林中的应用
  18. 恢复平衡0x8b,0xff,0x55,0x8b,0xec
  19. 量化分析师的Python日记【第3天:一大波金融Library来袭之numpy篇】
  20. Android 调用系统剪裁工具剪裁用户头像

热门文章

  1. PHP的递增递减运算符有哪些,递增/递减运算符
  2. 自己制作deb包(转)
  3. 关于在windows7系统下,win+上下左右组合键失效的问题
  4. 【论文小结】CFC 分类回归不一致问题
  5. 数据放在本地,心里才更踏实,滚雪球学 Python
  6. 树莓派微型计算机教程,一步即达——Lakka傻瓜教程
  7. 分治法求平面最近点对入门
  8. Windows程序内部运行原理(2)
  9. java poi exce 移动_JAVA_POI 操作Excel
  10. 嵌入式实时操作系统μC/OS-Ⅱ 在DSP芯片上的移植与测试