前言
這篇文章起源於在公司寫的一個PPT但是由於PPT本身的限制很多內容無法表達或是詳細的解釋於是變下定了決心寫篇文檔!
在這篇文章裡我將盡量簡單的描述下ADONET 的新特性尤其是配合SQL Server 所展現出來的強大實力如果想進一步了解ADONET 編程方面的話可以去閱讀Glenn Johnson的ADONET 高級編程[微軟推薦叢書] 定價:元網絡購書的話打了折只要塊就可以了
一功能強大的ADO
年底(年月)與 SQL Server 一起出現的是 NET Framework 版本其中用來訪問數據庫的 ADONET類也升級到 ADONET 版
ADONET 除了增強舊功能外也提供了相當多的新功能包含了以基礎類為本(baseclassbased)的數據源提供程序(provider)模型異步訪問架構批處理更新與大量數據復制(bulk copy)SQL Server 的回調通知單一連接同時多執行結果集(MARS)執行統計強化的 DataSet 類等等換句話說若要有效發揮 SQL Server 的功能前端應用程序最好用 ADONET 來開發
ADONET 提供了相當多的新增功能一些與數據源提供程序無關也就是訪問各種數據庫都可以用到的功能但有很大的一部分是專屬於 SQL Server 針對 SQL Server 的新功能提供給前端應用程序開發使用
二: 使用多數據結果集(僅限)
在之前版本的 SQL Server 同一時間一條連接只能傳遞一個 SELECT 語法執行後返回的結果集如果想在一次連接後返回多個查詢內容只能使用類似如下的方法來實現
SqlDataAdapter myDataAdapter = new SqlDataAdapter(StoredProcedureNamemyConnection);
myDataAdapterSelectCommandCommandType = CommandTypeStoredProcedure;
myDataAdapterSelectCommandParametersAdd(@sqlstrsqlstr);
DataSet ds = new DataSet();
myDataAdapterFill(ds);
return ds;
dsTables[]dsTables[]dsTables[]分別對應三個結果集
SQL Server 提供了在同一條連接上可以同時傳遞多個沒有游標結構(cursorless)的結果集(也稱為默認結果集)此功能稱為 Multiple Active Resultsets(MARS)如此可以節省需要同時打開的連接數但要注意的是連接字符串設置要加上 MultipleActiveResultSets=true 屬性否則默認不啟動多數據結果集的功能
string connstr = server=(local);database=northwind;integrated security=true; ;
SqlConnection conn = new SqlConnection(connstr);
connOpen();
SqlCommand cmd = new SqlCommand(select * from customers conn);
SqlCommand cmd = new SqlCommand(select * from orders conn);
SqlDataReader rdr = cmdExecuteReader();
// next statement causes an error prior to SQL Server
SqlDataReader rdr = cmdExecuteReader();
// now you can reader from rdr and rdr at the same time
三異步執行Command命令
在 ADONET 以前通過 Command 類(如 SqlCommandOleDbCommand等)執行 SQL
命令的線程一定要停下來等待執行結果ADONET 新增了異步程序訪問接口(asynchronous API)讓線程發出命令後可以繼續執行接下去的程序代碼
而在 ADONET 當前的版本只有 SqlClient 支持異步程序訪問接口
以往編寫程序時我們可以直接通過NET Framework 所提供的多線程機制或是以 Delegate 類包裝多線程的方式在 NET Framework 所提供的異步架構下設計調用執行 Command 對象實例這些方法都是讓一條工作線程(Worker Thread)停止在後台中等待執行結果一旦有結果後工作線程再通過標准的機制告知結果
原本 ADONET 的 Command 對象執行 SQL 語法的方法有
ExecuteReaderExecuteNonQueryExecuteXmlReader 以及 ExecuteScalar 等搭配 NET
Framework 原來就提供的異步模型慣例除了 ExecuteScalar 方法外其余的方法都新增了以 Begin 和 End 關鍵字開始的一對方法也就是說 ExecuteReader 方法是同步執行若要以異步的方式執行相同的功能則調用 BeginExecuteReader 和 EndExecuteReader 這一組方法在 NET Framework 中以 Begin 為字首的方法負責傳入同名方法所需的參數而以 End
為字首的方法用來取回執行結果
例如某個方法的定義如下
public override int ExecuteNonQuery()
則以異步調用的起始方法定義如下
public IAsyncResult BeginExecuteNonQuery(AsyncCallback callback object stateObject)
Begin~ 系列的方法會多加存放回調方法(Delegation)的指針參數也就是上述語法中的 callback 參數並提供語法中的 stateObject參數讓你設置想要帶到 End~ 對應方法的信息而 Begin~ 系列方法最後返回的是代表異步執行狀態的 IAsyncResult 對象實例而不是原本同步執行方法的返回結果你可以藉此查詢異步執行的狀況
而獲得執行結果的方法定義如下
public int EndExecuteNonQuery(IAsyncResult asyncResult)
在調用與 Begin~ 對應的 End~ 方法時需要帶入 Begin~ 方法所返回的 IAsyncResult
對象實例異步執行完畢後取回與原先同步執行方法相同的執行結果
由於我們在執行完 Command 對象訪問數據庫的方法後都會返回對象如 ExecuteReader 取回 DataReader實例ExecuteNonQuery 取回受影響的記錄條數ExecuteXmlReader 取回 XmlReader 實例因此大概都需要通過End系列方法來獲得執行結果否則這些結果就遺失在系統中
若要異步執行 Command 命令另一個必需設置的是數據庫連接字符串內要加上 async=true 屬性若連接字符串沒有加上該屬性而通過 Command對象實例調用異步執行的方法則會產生異常(exception)若 Command 通過連接執行時重頭到尾都是以同步的方式執行則依照默認 async=false 的方式設置比較節省資源若某些命令需要同步執行另一些需要異步執行則可以考慮使用不同的連接
在介紹范例應用程序前我們先稍微談一下 NET Framework 所提供的公共的異步運行應用程序設計模式不只是 ADONET在其他訪問耗時的程序編寫上也都可以套用這個模式
NET Framework內置了讓應用程序異步運行的功能讓你在編寫應用程序時不會因為某些耗時等待的操作讓程序停止響應操作界面停滯讓用戶感覺起來好像死機一樣一般會以多線程的方式處理這種需求但若你不熟悉線程的運行或是想利用線程池(Thread Pool)的好處都可以在較為耗時的操作上采用 NET Framework 所提供的異步功能
一般來說文件 I/O網絡訪問乃至於 Web Services 訪問以及本節所討論的 DB 訪問等都較為耗時NET Framework為這一類的類都提供了上述以 Begin~/End~開頭的非同步執行方法而這些方法皆成對出現當然也有可能是自己編寫的方法其商業邏輯非常復雜導致調用該方法後需要等待一段時間來完成這時還可以通過 NETFramework 所提供的委托(Delegate)類來創建異步運行
但是實際在我們的應用中
但我們不需要獲知DB服務器的返回信息時我們推薦使用委托尤其是在Web開發中
因為在頁面線程啟動異步數據庫訪問時當頁面業務執行完畢後仍然無法放開訪問數據庫的異步線程這是我們不希望看到的但是使用委托卻可以避免這個麻煩(webservice異步應用中一樣如此)
四使用SqlBulkCopy批量裝載數據(僅限SqlClient)
以往訪問 SQL Server 時若有大量的數據記錄需要添加到數據庫內例如從主機系統或是 NCR TeradataOracle等數據庫系統下載大量數據記錄我們想要將它們快速添加到 SQL Server 中可以有的選擇是調用 TSQL 的 Bulk Insert 語法通過Linked Server 執行 SELECT INTO 語法或是執行 bcpexe 工具程序以及通過 DTS 的 Bulk Insert Task 或啟動Transform Data Task 的快速裝載(Use Fast Load)設置
但若要通過自行編寫的程序完成批次裝載只能以 C/C++ 調用 OLEDB 或 ODBC 的 Bulk API無法通過 ADONET 或 ADO 等對象來執行
ADONET 的 SqlClient 提供了一個新的類稱為 SqlBulkCopy它讓 DataSet 內大量的數據或是 DataReader通過數據流(Stream)直接讀取大量的記錄可以快速將這些記錄添加到目的數據庫的數據表中但要注意的是它並非如我們一般用的 bcpexe工具程序可以從某個符號分隔文件讀取大量數據選擇性地搭配格式文件(Format File)將記錄裝載到數據庫中或是將數據庫內的數據導出成為一個文件但由於DataSet 能集成 XML 數據因此依然可以采用 SqlBulkCopy 類型輕松地通過 DataSet 將 XML 文件數據大量轉入到數據庫
可以利用SqlBulkCopy類快速寫入大批量數據針對SQL Server的優化可以寫入DataRow數據DataTableDataReader並且可以映射不同的數據列名
WriteToServer(DataTable)寫入數據表
WriteToServer(DataRow[])批次寫入數據行
WriteToServer(DataTable DataRowState)按行狀態寫入數據庫表
WriteToServer(IDataReader)寫入DataReader對象
下面是個示例
using (SqlConnection sqlcon =
new SqlConnection(Data Source=;user id=oa;password=oapassword;initial catalog=test))
{
sqlconOpen();
using (SqlBulkCopy bcp = new SqlBulkCopy(sqlcon))
{
bcpBulkCopyTimeout = ;
bcpDestinationTableName = dboTest;
bcpColumnMappingsAdd(id id);
bcpColumnMappingsAdd(name name);
bcpColumnMappingsAdd(name name);
bcpColumnMappingsAdd(name name);
//映射到不同名列
bcpColumnMappingsAdd(changedname name);
bcpWriteToServer(dt);
sqlconClose();
}
}
但是SqlBulkCopy使用時要注意以下幾點
.確認確實需要大容量更新在執行此操作(幾十行的數據請盡量使用別的渠道把)
.
.確認數據一致性與檢查機制以免遇到主鍵沖突數據不符格式等意外
.
.SqlBulkCopy操作可能會導致對目標表元數據的更改(例如禁用約束檢查時)如果出現這種情況訪問大容量插入表的並發快照隔離事務將失敗
SqlBulkCopy將向數據庫下大容量更新鎖請注意並發性以免其他連接因長時間等待而超時
五DataSet的性能提升
對於開發人員來說ADONET 最激動人心的變化莫過開發組終於實現了他們許諾多年的事情確實提升Dataset的性能了
由於版本Dataset令人不敢恭維的性能使得Dataset許多方面被其性能問題而掩蓋
現在在大幅度提升了Dataset的性能後Dataset終於能日趨完美了
提升是多方面的被提升的方面包括下面幾塊
. 索引引擎被大大的提升
在對ADONET 的Dataset作了相當數目的測試後微軟終於宣布Dataset的數據訪問能力獲得極大的提高廣泛的數字是增加倍!!!而且不像中排序的陡峭曲線中的排序盡量做到了線型遞增!
真不知小組是以前做的實在太爛了還是有了新的狠招被發明了^_^
二進序列制化的Dataset
Dataset有個好屬性是支持序列化但是有很多人對其提出了批評不是這些人不喜歡Dataset支持序列化而是序列化後的Dataset真的是太肥了大量的
<xs:element name=type…占用了大量的空間以至讓人難以忍受
現在我們只需要設置 RemotingFormat 屬性為SerializationFormatBinary(默認是SerializationFormatXML)則在序列化時完全采用二進制的數據格式如此數據較小因而較有效率(官方的觀點是縮為SerializationFormatXML的/)
From:http://tw.wingwit.com/Article/program/net/201311/11944.html