日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

一个有趣的问题, 你知道SqlDataAdapter中的Fill是怎么实现的吗

發(fā)布時(shí)間:2023/12/4 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一个有趣的问题, 你知道SqlDataAdapter中的Fill是怎么实现的吗 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一:背景

1. 講故事

最近因?yàn)楦鞣矫嬖驌Q了一份工作,去了一家主營物聯(lián)柜的公司,有意思的是物聯(lián)柜上的終端是用 wpf 寫的,代碼也算是年久失修,感覺技術(shù)債還是蠻重的,前幾天在調(diào)試一個(gè)bug的時(shí)候,看到了一段類似這樣的代碼:

var dt = new DataTable();SqlDataAdapter adapter = new SqlDataAdapter(new SqlCommand());adapter.Fill(dt);

是不是很眼熟哈,或許你也已經(jīng)多年不見了,猶記得那時(shí)候?yàn)榱四軓臄?shù)據(jù)庫獲取數(shù)據(jù),第一種方法就是采用?SqlDataReader?一行一行從數(shù)據(jù)庫讀取,而且還要操心 Reader 的 close 問題,第二種方法為了避免麻煩,就直接使用了本篇說到的?SqlDataAdapter?,簡單粗暴,啥也不用操心,對了,不知道您是否和我一樣對這個(gè)?Fill?方法很好奇呢?,它是如何將數(shù)據(jù)塞入到?DataTable?中的呢?也是用的 SqlDataReader 嗎?而且?Fill?還有好幾個(gè)擴(kuò)展方法,哈哈,本篇就逐個(gè)聊一聊,就當(dāng)回顧經(jīng)典啦!

二:對Fill方法的探究

1. 使用 dnspy 查看Fill源碼

dnspy小工具大家可以到GitHub上面去下載一下,這里就不具體說啦,接下來追一下Fill的最上層實(shí)現(xiàn),如下代碼:

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;}

上面的代碼比較關(guān)鍵的一個(gè)地方就是?IDbCommand selectCommand = this._IDbDataAdapter.SelectCommand;?這里的 SelectCommand 來自于哪里呢?來自于你 new SqlDataAdapter 的時(shí)候塞入的構(gòu)造函數(shù) SqlCommand,如下代碼:

public SqlDataAdapter(SqlCommand selectCommand) : this(){this.SelectCommand = selectCommand;}

然后繼續(xù)往下看 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;}

上面這段代碼沒啥好說的,繼續(xù)往下追蹤?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;}

大家可以仔細(xì)研讀一下上面的代碼,挺有意思的,至少你可以獲取以下兩點(diǎn)信息:

  • 從各個(gè) finally 中可以看到,當(dāng)數(shù)據(jù) fill 到 datatable 中之后,操作數(shù)據(jù)庫的幾大對象?Connection,Transaction,DataReader?都會(huì)進(jìn)行關(guān)閉,你根本不需要操心。

  • this.Fill(datatables, dataReader, startRecord, maxRecords)?中可以看到,底層不出意外也是通過?dataReader.read()?一行一行讀取然后塞到?DataTable中去的,不然它拿這個(gè) dataReader 干嘛呢?不信的話可以繼續(xù)往下追。

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,從而方便生成多個(gè) datatable 對象,最后就是填充 DataTable ,當(dāng)然就是用?dataReader.Read()啦,不信你可以一直往里面追嘛,如下代碼:

private int FillLoadDataRow(SchemaMapping mapping){int num = 0;DataReaderContainer dataReader = mapping.DataReader;while (dataReader.Read()){mapping.LoadDataRow();num++;}return num;}

到這里你應(yīng)該意識(shí)到:DataReader 的性能肯定比 Fill 到 DataTable 要高的太多,所以它和靈活性兩者之間看您取舍了哈。

二:Fill 的其他重載方法

剛才給大家介紹的是帶有 DataTable 參數(shù)的重載,其實(shí)除了這個(gè)還有另外四種重載方法,如下圖:

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()?是只讀向前的,然后一起看一下源碼底層是怎么實(shí)現(xiàn)的。

從上圖中可以看出,還是很簡單的哈,踢掉 startRecord 個(gè) reader(),然后再只讀向前獲取最多 maxRecords 條記錄。

2. dataSet 和 srcTable

這里的 srcTable 是什么意思呢?從 vs 中看是這樣的:?The name of the source table to use for table mapping.?乍一看也不是特別清楚,沒關(guān)系,我們直接看源碼就好啦,反正我也沒測試,嘿嘿。

從上圖中你應(yīng)該明白大概意思就是給你 dataset 中的 datatable 取名字,比如:name= 學(xué)生表, 那么database中的的 tablename依次是:?學(xué)生表,學(xué)生表1,學(xué)生表2 ..., 這樣你就可以索引獲取表的名字了哈,如下代碼所示:

DataSet dataSet = new DataSet();dataSet.Tables.Add(new DataTable("學(xué)生表"));var tb = dataSet.Tables["學(xué)生表"];

四:總結(jié)

本篇就聊這么多吧,算是解了多年之前我的一個(gè)好奇心,希望本篇對您有幫助。

總結(jié)

以上是生活随笔為你收集整理的一个有趣的问题, 你知道SqlDataAdapter中的Fill是怎么实现的吗的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。