Dapper的效率问题的总结
前言:
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的效率问题的总结相关推荐
- bltoolkit mysql_.NET 轻量级 ORM 框架 - Dapper 介绍
转自:https://blog.csdn.net/hanjun0612/article/details/52170204 Dapper简单介绍: Dapper is a single file you ...
- 关于Dapper.NET的相关论述
年少时,为何不为自己的梦想去拼搏一次呢?纵使头破血流,也不悔有那年少轻狂.感慨很多,最近事情也很多,博客也很少更新了,毕竟每个人都需要为自己的生活去努力. 最近在一个群里遇到一个人说的话,在这里不再赘 ...
- OSS.Core基于Dapper封装(表达式解析+Emit)仓储层的构思及实现
最近趁着不忙,在构思一个搭建一个开源的完整项目,至于原因以及整个项目框架后边文章我再说明.既然要起一个完整的项目,那么数据仓储访问就必不可少,这篇文章我主要介绍这个新项目(OSS.Core)中我对仓储 ...
- .net core 基于Dapper 的分库分表开源框架(core-data)
一.前言 感觉很久没写文章了,最近也比较忙,写的相对比较少,抽空分享基于Dapper 的分库分表开源框架core-data的强大功能,更好的提高开发过程中的效率:在数据库的数据日积月累的积累下,业务数 ...
- 同时支持EF+Dapper的混合仓储,助你快速搭建数据访问层
背景 17年开始,公司开始向DotNet Core转型,面对ORM工具的选型,当时围绕Dapper和EF发生了激烈的讨论.项目团队更加关注快速交付,他们主张使用EF这种能快速开发的ORM工具:而在线业 ...
- 深入Dapper.NET源码
经过业界前辈.StackOverflow多年推广,「Dapper搭配Entity Framework」成为一种功能强大的组合,它满足「安全.方便.高效.好维护」需求. 但目前中文网路文章,虽然有很多关 ...
- 开源Dapper的Lambda扩展-Sikiro.Dapper.Extension V2.0
去年我在业余时间,自己整了一套dapper的lambda表达式的封装,原本是作为了一个个人的娱乐项目,当时也只支持了Sql Server数据库.随之开源后,有不少朋友也对此做了试用,也对我这个项目提出 ...
- PocoClassGenerator:RDBMS所有表/视图生成Dapper POCO类代码
目录 介绍 开始 第一 第二 演示图片 域逻辑 域代码 下载Github链接 介绍 制作PocoClassGenerator的原因是我经常需要" 生成大量RDBMS的表/视图到dapper ...
- 02-大鸭梨博客系统数据库设计及Dapper的使用
毫无疑问,数据库的设计在一个系统中起了至关重要的作用.我们都知道,系统设计分为两部分,或者说是两个阶段,即数据库设计和功能设计.构建一个完善的系统需要这两个阶段的充分考量.周密设计.合理联接以及密切配 ...
最新文章
- Tarjan无向图连通性
- android远程控制灯光,智能灯具如何实现远程控制技术
- python按时间排序 文件
- Win64 驱动内核编程-17. MINIFILTER(文件保护)
- uva 10716——Evil Straw Warts Live
- 1381. 设计一个支持增量操作的栈
- 步步惊芯 软核处理器的内部设计分析 pdf_【深度分析】互联网巨头造“芯”现状...
- el 能否定义作用域变量_Python 基础知识全篇-命名空间和作用域
- 解决:error: Microsoft Visual C++ 14.0 or greater is required. Get it with “Microsoft C++ Build Tools“
- 自作孽!知网终于被查了,涉嫌垄断,背后到底是何方神圣?
- 解决VM虚拟机启动后假死
- 【转载】浅的不能再浅的浅谈百利金单双胡及全字尖
- 容器云的双活与灾备技术
- 淘宝手机端详情页的设置
- 十大实用网站推荐(1)
- 抖音用计算机怎么表白,抖音表白代码
- python在园林中的应用_浅析亭在园林中的应用
- 恢复平衡0x8b,0xff,0x55,0x8b,0xec
- 量化分析师的Python日记【第3天:一大波金融Library来袭之numpy篇】
- Android 调用系统剪裁工具剪裁用户头像