一个有趣的问题, 你知道SqlDataAdapter中的Fill是怎么实现的吗
一:背景
1. 讲故事
最近因为各方面原因换了一份工作,去了一家主营物联柜的公司,有意思的是物联柜上的终端是用 wpf 写的,代码也算是年久失修,感觉技术债还是蛮重的,前几天在调试一个bug的时候,看到了一段类似这样的代码:
var dt = new DataTable();
SqlDataAdapter adapter = new SqlDataAdapter(new SqlCommand());
adapter.Fill(dt);
是不是很眼熟哈,或许你也已经多年不见了,犹记得那时候为了能从数据库获取数据,第一种方法就是采用 SqlDataReader 一行一行从数据库读取,而且还要操心 Reader 的 close 问题,第二种方法为了避免麻烦,就直接使用了本篇说到的 SqlDataAdapter ,简单粗暴,啥也不用操心,对了,不知道您是否和我一样对这个 Fill 方法很好奇呢?,它是如何将数据塞入到 DataTable 中的呢? 也是用的 SqlDataReader 吗? 而且 Fill 还有好几个扩展方法,哈哈,本篇就逐个聊一聊,就当回顾经典啦!
二:对Fill方法的探究
1. 使用 dnspy 查看Fill源码
dnspy小工具大家可以到GitHub上面去下载一下,这里就不具体说啦,接下来追一下Fill的最上层实现,如下代码:
public int Fill(DataTable dataTable)
{
IntPtr intPtr;
Bid.ScopeEnter(out intPtr, "<comm.DbDataAdapter.Fill|API> %d#, dataTable\n", base.ObjectID);
int result;
try
{
DataTable[] dataTables = new DataTable[]
{
dataTable
};
IDbCommand selectCommand = this._IDbDataAdapter.SelectCommand;
CommandBehavior fillCommandBehavior = this.FillCommandBehavior;
result = this.Fill(dataTables, 0, 0, selectCommand, fillCommandBehavior);
}
finally
{
Bid.ScopeLeave(ref intPtr);
}
return result;
}
上面的代码比较关键的一个地方就是 IDbCommand selectCommand = this._IDbDataAdapter.SelectCommand; 这里的 SelectCommand 来自于哪里呢? 来自于你 new SqlDataAdapter 的时候塞入的构造函数 SqlCommand,如下代码:
public SqlDataAdapter(SqlCommand selectCommand) : this()
{
this.SelectCommand = selectCommand;
}
然后继续往下看 this.Fill 方法,代码简化后如下:
protected virtual int Fill(DataTable[] dataTables, int startRecord, int maxRecords, IDbCommand command, CommandBehavior behavior)
{
result = this.FillInternal(null, dataTables, startRecord, maxRecords, null, command, behavior);
return result;
}
上面这段代码没啥好说的,继续往下追踪 this.FillInternal 方法,简化后如下:
private int FillInternal(DataSet dataset, DataTable[] datatables, int startRecord, int maxRecords, string srcTable, IDbCommand command, CommandBehavior behavior)
{
int result = 0;
try
{
IDbConnection connection = DbDataAdapter.GetConnection3(this, command, "Fill");
try
{
IDataReader dataReader = null;
try
{
dataReader = command.ExecuteReader(behavior);
result = this.Fill(datatables, dataReader, startRecord, maxRecords);
}
finally
{
if (dataReader != null) dataReader.Dispose();
}
}
finally
{
DbDataAdapter.QuietClose(connection, originalState);
}
}
finally
{
if (flag)
{
command.Transaction = null;
command.Connection = null;
}
}
return result;
}
大家可以仔细研读一下上面的代码,挺有意思的,至少你可以获取以下两点信息:
从各个 finally 中可以看到,当数据 fill 到 datatable 中之后,操作数据库的几大对象
Connection,Transaction,DataReader都会进行关闭,你根本不需要操心。this.Fill(datatables, dataReader, startRecord, maxRecords)中可以看到,底层不出意外也是通过dataReader.read()一行一行读取然后塞到DataTable中去的,不然它拿这个 dataReader 干嘛呢? 不信的话可以继续往下追。
protected virtual int Fill(DataTable[] dataTables, IDataReader dataReader, int startRecord, int maxRecords)
{
try
{
int num = 0;
bool flag = false;
DataSet dataSet = dataTables[0].DataSet;
int num2 = 0;
while (num2 < dataTables.Length && !dataReader.IsClosed)
{
DataReaderContainer dataReaderContainer = DataReaderContainer.Create(dataReader, this.ReturnProviderSpecificTypes);
if (num2 == 0)
{
bool flag2;
do
{
flag2 = this.FillNextResult(dataReaderContainer);
}
while (flag2 && dataReaderContainer.FieldCount <= 0);
}
}
result = num;
}
return result;
}
从上面代码可以看到, dataReader 被封装到了 DataReaderContainer 中,用 FillNextResult 判断是否还有批语句sql,从而方便生成多个 datatable 对象,最后就是填充 DataTable ,当然就是用 dataReader.Read()啦,不信你可以一直往里面追嘛,如下代码:
private int FillLoadDataRow(SchemaMapping mapping)
{
int num = 0;
DataReaderContainer dataReader = mapping.DataReader;
while (dataReader.Read())
{
mapping.LoadDataRow();
num++;
}
return num;
}
到这里你应该意识到: DataReader 的性能肯定比 Fill 到 DataTable 要高的太多,所以它和灵活性两者之间看您取舍了哈。
二:Fill 的其他重载方法
刚才给大家介绍的是带有 DataTable 参数的重载,其实除了这个还有另外四种重载方法,如下图:
public override int Fill(DataSet dataSet);
public int Fill(DataSet dataSet, string srcTable);
public int Fill(DataSet dataSet, int startRecord, int maxRecords, string srcTable);
public int Fill(int startRecord, int maxRecords, params DataTable[] dataTables);
1. startRecord 和 maxRecords
从字面意思看就是想从指定的位置 (startRecord) 开始读,然后最多读取 maxRecords 条记录,很好理解,我们知道 reader() 是只读向前的,然后一起看一下源码底层是怎么实现的。

从上图中可以看出,还是很简单的哈,踢掉 startRecord 个 reader(),然后再只读向前获取最多 maxRecords 条记录。
2. dataSet 和 srcTable
这里的 srcTable 是什么意思呢? 从 vs 中看是这样的: The name of the source table to use for table mapping. 乍一看也不是特别清楚,没关系,我们直接看源码就好啦,反正我也没测试,嘿嘿。

从上图中你应该明白大概意思就是给你 dataset 中的 datatable 取名字,比如:name= 学生表, 那么database中的的 tablename依次是: 学生表,学生表1,学生表2 ..., 这样你就可以索引获取表的名字了哈,如下代码所示:
DataSet dataSet = new DataSet();
dataSet.Tables.Add(new DataTable("学生表"));
var tb = dataSet.Tables["学生表"];
四:总结
本篇就聊这么多吧,算是解了多年之前我的一个好奇心,希望本篇对您有帮助。
如您有更多问题与我互动,扫描下方进来吧~

一个有趣的问题, 你知道SqlDataAdapter中的Fill是怎么实现的吗的更多相关文章
- 分享一个有趣的代码,调用电脑中的api语音
在文本文件中输入如下代码: set objTTS = CreateObject("sapi.spvoice") objTTS.speak("为啥我对象这么闹呢?" ...
- dubbo debug过程中一个有趣的问题
最近在debug dubbo代码过程中遇到的很有趣的问题 我们都知道dubbo ReferenceBean是消费者的spring bean包装,为了查一个consumer端的问题,在Reference ...
- 【小贴士】关于transitionEnd/animate的一个有趣故事
前言 在很久之前,我们项目有一个动画功能,功能本身很简单,便是典型的右进左出,并且带动画功能 以当时来说,虽然很简单,但是受限于框架本身的难度,就直接使用了CSS3的方式完成了功能 当时主要使用tra ...
- 一个有趣的SQL Server 层级汇总数据问题
看SQL Server大V宋大侠的博客文章,发现了一个有趣的sql server层级汇总数据问题. 具体的问题如下: parent_id emp_id emp_nam ...
- 一个有趣的模拟光照的shader
一个有趣的模拟光照的shader(类似法线贴图) http://www.cnblogs.com/flytrace/p/3395911.html ----- 可否用于需UI中需要加灯的模型.
- 一个有趣的 SQL 查询(查询7天连续登陆)
一个有趣的 SQL 查询 一个朋友有这样一个SQL查询需求: 有一个登录表(tmp_test),包含用户ID(uid)和登录时间(login_time).表结构如下: . row ********** ...
- 一个有趣的小例子,带你入门协程模块-asyncio
一个有趣的小例子,带你入门协程模块-asyncio 上篇文章写了关于yield from的用法,简单的了解异步模式,[https://www.cnblogs.com/c-x-a/p/10106031. ...
- 一个有趣的js隐式转换的问题
一个有趣的js隐式转换的问题 在chrome的控制台中打印一下表达式 [] + {} //结果为 [object object] 然后调整顺序打印 {} + [] //结果为 0 然后将两个表达式组合 ...
- 举一个有趣的例子,让你轻松搞懂JVM内存管理
目录 前言 例子 源码 输出 图解 深入分析 学以致用 写在最后 前言 在JAVA虚拟机内存管理中,堆.栈.方法区.常量池等概念经常被提到,对理论知识的理解也常常停留在字面意思上,比如说堆内存中存放对 ...
随机推荐
- Python3-logging模块-日志记录
Python3中的logging模块提供了较为灵活的事件日志系统 日志级别 DEBUG < INFO < WARING(Python默认) < ERROR < FATAL(CR ...
- 手摸手带你理解Vue的Computed原理
前言 computed 在 Vue 中是很常用的属性配置,它能够随着依赖属性的变化而变化,为我们带来很大便利.那么本文就来带大家全面理解 computed 的内部原理以及工作流程. 在这之前,希望你能 ...
- 作为一个Java开发你用过Jib吗
1. 前言 Jib是Google开发的可以直接构建 Java应用的Docker和OCI镜像的类库,以Maven和Gradle插件形式提供.它最骚操作的是可以在没有Docker守护程序的情况下构建,也就 ...
- Python实用笔记 (22)面向对象编程——实例属性和类属性
由于Python是动态语言,根据类创建的实例可以任意绑定属性. 给实例绑定属性的方法是通过实例变量,或者通过self变量: class Student(object): def __init__(se ...
- mac下创建安卓应用 hello-world
教程 https://www.jianshu.com/p/bf77cb5ce70b 需要注意的地方 jdk目录查找 jdk目录拷贝到tool目录下面(jdk可以拷贝,没有其他牵扯) https://w ...
- 使用CImage双缓冲
一普通显示:现在的VC显示图片非常方便,远不是VC6.0那个年代的技术可比,而且支持多种格式的如JPG,PNG. CImage _img; 初始化: _img.Load(L"map.png& ...
- 服务消费者(Ribbon)
上一篇文章,简单概述了服务注册与发现,在微服务架构中,业务都会被拆分成一个独立的服务,服务之间的通讯是基于http restful的,Ribbon可以很好地控制HTTP和TCP客户端的行为,Sprin ...
- C++ 自动资源释放的单例模式
代码思想就是程序结束时会自动释放静态/全局资源时删除单例资源. //.h class CSingleton { public: static CSingleton* Singleton() { ret ...
- css3盒子模型中的box-sizing属性的使用_box sizing常用的3种属性
box-sizing属性用来定义元素的width和height所表示的区域,该属性一般有三种值:content-box.border-box.inherit.其中inherit表示box-sizing ...
- 20 个 CSS高级样式技巧汇总
使用技巧会让人变的越来越懒,没错,我就是想让你变懒.下面是我收集的CSS高级技巧,希望你懒出境界. 1. 黑白图像 这段代码会让你的彩色照片显示为黑白照片,是不是很酷? img.desaturate ...