服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - c# SqlDataAdapter中的Fill是怎么实现的

c# SqlDataAdapter中的Fill是怎么实现的

2022-09-28 15:39一线码农 C#

这篇文章主要介绍了c# SqlDataAdapter中的Fill是怎么实现的,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下

1. 讲故事

最近因为各方面原因换了一份工作,去了一家主营物联柜的公司,有意思的是物联柜上的终端是用 wpf 写的,代码也算是年久失修,感觉技术债还是蛮重的,前几天在调试一个bug的时候,看到了一段类似这样的代码:

?
1
2
3
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的最上层实现,如下代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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,如下代码:

?
1
2
3
4
public SqlDataAdapter(SqlCommand selectCommand) : this()
    {
        this.SelectCommand = selectCommand;
                          }

然后继续往下看 this.Fill 方法,代码简化后如下:

?
1
2
3
4
5
6
  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 方法,简化后如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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 干嘛呢? 不信的话可以继续往下追。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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()啦,不信你可以一直往里面追嘛,如下代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
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 参数的重载,其实除了这个还有另外四种重载方法,如下图:

?
1
2
3
4
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() 是只读向前的,然后一起看一下源码底层是怎么实现的。

c# SqlDataAdapter中的Fill是怎么实现的

从上图中可以看出,还是很简单的哈,踢掉 startRecord 个 reader(),然后再只读向前获取最多 maxRecords 条记录。

2. dataSet 和 srcTable

这里的 srcTable 是什么意思呢? 从 vs 中看是这样的: The name of the source table to use for table mapping. 乍一看也不是特别清楚,没关系,我们直接看源码就好啦,反正我也没测试,嘿嘿。

#9d5141fe0e32ee8dd839c42d72c6c83c#

从上图中你应该明白大概意思就是给你 dataset 中的 datatable 取名字,比如:name= 学生表, 那么database中的的 tablename依次是: 学生表,学生表1,学生表2 ..., 这样你就可以索引获取表的名字了哈,如下代码所示:

?
1
2
3
DataSet dataSet = new DataSet();
dataSet.Tables.Add(new DataTable("学生表"));
var tb = dataSet.Tables["学生表"];

四:总结

本篇就聊这么多吧,算是解了多年之前我的一个好奇心,希望本篇对您有帮助。

以上就是c# SqlDataAdapter中的Fill是怎么实现的的详细内容,更多关于c# SqlDataAdapter的资料请关注服务器之家其它相关文章!

原文链接:https://www.cnblogs.com/huangxincheng/p/13358901.html

延伸 · 阅读

精彩推荐
  • C#C#禁止textbox复制、粘贴、剪切及鼠标右键的方法

    C#禁止textbox复制、粘贴、剪切及鼠标右键的方法

    这篇文章主要介绍了C#禁止textbox复制、粘贴、剪切及鼠标右键的方法,涉及C#针对窗口消息的处理技巧,具有一定参考借鉴价值,需要的朋友可以参考下...

    我心依旧11992021-10-25
  • C#如何使用C#从word文档中提取图片

    如何使用C#从word文档中提取图片

    图片和文字是word文档中两种最常见的对象,在微软word中,如果我们想要提取出一个文档内的图片,只需要右击图片选择另存为然后命名保存就可以了,今...

    C#教程网11742021-11-12
  • C#C#如何给word文档添加水印

    C#如何给word文档添加水印

    这篇文章主要为大家详细介绍了C#如何给word文档添加水印的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    Yesi10162021-12-07
  • C#C#开发微信门户及应用(5) 用户分组信息管理

    C#开发微信门户及应用(5) 用户分组信息管理

    这篇文章主要为大家详细介绍了C#开发微信门户及应用第五篇,用户分组信息管理,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    伍华聪9812022-01-10
  • C#C# 获取进程退出代码的实现示例

    C# 获取进程退出代码的实现示例

    这篇文章主要介绍了C# 获取进程退出代码的实现示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    林德熙3802022-03-09
  • C#C#获取App.Config配置项的方法总结

    C#获取App.Config配置项的方法总结

    在本篇内容里小编给大家分享了C#获取App.Config配置项的方法和相关知识点,需要的朋友们学习下。...

    C#教程网6252022-07-16
  • C#C# 在PDF文档中创建表格的实现方法

    C# 在PDF文档中创建表格的实现方法

    表格能够一目了然的让用户看到数据信息,使信息显得有条理化,那么在pdf类型的文档中如何来添加表格并对表格进行格式化操作呢?下面小编给大家带来...

    E-iceblue11422022-02-17
  • C#C#日期格式强制转换方法(推荐)

    C#日期格式强制转换方法(推荐)

    下面小编就为大家分享一C#日期格式强制转换的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    王浩祥9892022-02-12