閱讀概要 歡迎參予討論MARS的使用!通過這裡介紹的MARS技術上,你能夠在單個連接上執行多重數據庫查詢。而且依賴於MARS技術的編碼更為簡單易讀,並且使你在開發數據集中的Web應用程序時可以減少內存使用並在一定程序上消除性能瓶頸。難道你不想嘗試一下MARS?
多活動結果集(Multiple Active Result Sets,簡稱MARS)是ADO.NET 2.0的一個新特征-它允許在單個連接上執行多重的數據庫查詢或存儲過程。這樣的結果是,你能夠在單個連接上得到和管理多個、僅向前引用的、只讀的結果集。在MARS以前,針對每個結果集需要一個獨立的連接。當然,你還必須管理這些連接並且要付出相應的內存和潛在的應用程序中的高度擁擠的瓶頸代價-特別是在數據集中的Web應用程序中。
第一個支持MARS的商業數據庫是SQL Server 2005,在本文中你將會詳細了解怎樣使用這一強有力的然而卻是易於添加到ADO.NET開發中去的工具。
當在以前版本的SQL Server中存取數據結果集時,你需要創建額外的SqlConnection對象以配合使用SqlCommand。你需要追蹤你的所有結果集及其導致低效率程序的SqlConnections連接以及"There is already an open DataReader associated with this Connection"錯誤。現在,使用了MARS技術後,所有這些都成了過去。
一、 准備工作
最開始,你需要設置一新的連接字符串以啟動多個活動連接。自然地,我們把這個設置命名為"MultipleActiveResultSets",並且如下使用之:
String connectionString = "Data Source=DBSERVER;" +
"Initial Catalog=AdventureWorlds;IntegratedSecurity=SSPI;" +
"MultipleActiveResultSets=True";
它的默認設定是"False",並且你能明確地禁用它-如果你想傳遞"False"給這個MultipleActiveResultSets設定的話。
請遵循下面步驟來建立和啟動MARS和ADO.NET 2.0:
1. 創建一個如上面所示的連接字符串。
2. 創建一個SqlConnection對象並且用該連接字符串初始化它。
3. 使用它的Open()方法打開這個SqlConnection對象。
4. 對於你想要執行的每個查詢,創建一個新的SqlCommand對象。把它們與你在上面創建並打開的SqlConnection對象相關聯。
5. 使用適當的命令(如,如果你想讀該取查詢結果的話,可以使用ExecuteReader();或使用ExecuteNonQuery()來進行更新,等等)來執行查詢。
6. 完成後,關閉SqlConnection對象。
二、實戰
在實際開發中普遍存在的一種典型的從數據庫中讀寫數據的情形是,你可以使用多重連接而現在只用一個連接就足夠了。例如,如果你有一些來自於幾個表中的數據-它們不能被聯結到一個查詢中,那麼你就會有多重的連接-每個連接都有一個與之相關連的命令用於讀取數據。同樣,如果你正在向一個表寫數據,那麼你需要另外一個連接或連接集合-如果有多個表要被更新的話。
可以考慮這樣的情形-你需要讀取來自兩個查詢A和B中的數據-從可能要寫向表C的數據獲取值,並且對你要寫向表D的行為保持一個審計記錄。在如此情形中,你的代碼可能如下:
·為A打開一個連接
·執行查詢A,並且填充一個數據集
·為A關閉一個連接
·為B打開一個連接
·執行查詢B,並且填充一個數據集
·為B關閉一個連接
·為C打開一個連接
·為D打開一個連接
·用從A和B中取得的詳細數據更新C
·用做過的審計標記更新D
·關閉D
·關閉C
這是相當復雜的!
當使用MARS時,你只要完成如下:
·用"MultipleActiveResultSets=true"打開連接
·執行A並且填充一個數據集
·執行B並且填充一個數據集
·用從A和B中取得的詳細數據更新C
·用做過的審計標記更新D
·關閉連接
更簡單!
三、基於MARS和C#的示例分析
本示例使用了隨同SQL Server 2005一起發行的AdventureWorks示例數據庫,並且使用了該數據庫的開發版。注意,它還要改變該庫的一些字段的內容,因此如果你想把這個示例數據庫用於別的目的,請注意這一點。
本示例將展示怎樣從數據庫中讀取一個SalesOrder,然後減少已賣出的項目的庫存數額。典型地,這將要求建立到數據庫的兩個順序連接-一個用於讀取售出的項目數額,另一個用於使用減少的數額來更新庫存。
下面的代碼片斷顯示了怎樣在不使用MARS功能的情況下達到這一目的。
ArrayList ids = new ArrayList();
ArrayList qtys = new ArrayList();
string connectionString = "Data Source=MEDIACENTER;" +
"Initial Catalog=AdventureWorks;Integrated Security=SSPI;" +
"MultipleActiveResultSets=False";
string strSQLGetOrder = "Select * from Sales.SalesOrderDetail" +
"WHERE SalesOrderID = 43659";
SqlConnection readConnection = new SqlConnection(connectionString);
readConnection.Open();
SqlCommand readCommand =new SqlCommand(strSQLGetOrder, readConnection);
using (SqlDataReader rdr = readCommand.ExecuteReader()){
while (rdr.Read()){
ids.Add(rdr["ProductID"]);
qtys.Add(rdr["OrderQty"]);
}
}
readConnection.Close();
string strSQLUpdateInv = "UPDATE Production.ProductInventory " +
"SET Quantity=Quantity-@amt WHERE (ProductID=@pid)";
SqlConnection writeConnection = new SqlConnection(connectionString);
writeConnection.Open();
SqlCommand writeCommand = new SqlCommand(strSQLUpdateInv,writeConnection);
writeCommand.Parameters.Add("@amt",SqlDbType.Int);
writeCommand.Parameters.Add("@pid",SqlDbType.Int);
for(int lp=0;lp<ids.Count;lp++){
writeCommand.Parameters["@amt"].Value=qtys[lp];
writeCommand.Parameters["@pid"].Value=ids[lp];
writeCommand.ExecuteNonQuery();
}
writeConnection.Close();
這個示例從數據庫中讀取單個銷售訂單(這裡,訂單號43659被硬編碼)-該庫中有一項目列表。這些項目應該從庫存中扣除,而這是通過第二個連接完成的。然而,為了建立在第二個連接上的正確查詢-從相應的產品中扣除正確的數量-要求第一個查詢的結果在內存中被緩沖。並且在這個示例中這是通過使用兩個數組列表來完成的。這裡的明顯耗費是:如果假定這是一個高度擁擠的網站,那麼我們需要大量的緩沖內存來處理這些最終要被扔掉的數值。
為此,你還有另外一個方法-通過同時打開兩個連接並且使用從一連接中讀取的數據結果-該連接被直接傳遞到在第二個連接上的更新命令;但是仍存在在打開多個連接時內存和數據庫方面的代價。典型地,數據庫連接對於一個應用程序來說比內存具有更高的代價,所以這裡使用了順序連接方式。
MARS提供了解決這個問題的在以上兩個方面均能達到最優的方法。你可以保持單個連接打開著,從而減少了到數據庫的所有連接。這樣以來,你就不需要用一個內存變量來存儲讀取的結果。
而且,該MARS代碼也為更短並且因此更易於讀取和維護。下面的代碼片斷展示了在相同的操作上使用MARS的情況:
string connectionString = "Data Source=MEDIACENTER;" +
"Initial Catalog=AdventureWorks;Integrated Security=SSPI;" +
"MultipleActiveResultSets=True";
string strSQLGetOrder = "Select * from Sales.SalesOrderDetail" +
"WHERE SalesOrderID = 43659";
string strSQLUpdateInv = "UPDATE Production.ProductInventory " +
"SET Quantity=Quantity-@amt WHERE (ProductID=@pid)";
SqlConnection marsConnection = new SqlConnection(connectionString);
marsConnection.Open();
SqlCommand readCommand = new SqlCommand(strSQLGetOrder, marsConnection);
SqlCommand writeCommand = new SqlCommand(strSQLUpdateInv, marsConnection);
writeCommand.Parameters.Add("@amt", SqlDbType.Int);
writeCommand.Parameters.Add("@pid", SqlDbType.Int);
using (SqlDataReader rdr = readCommand.ExecuteReader()){
while (rdr.Read()){
writeCommand.Parameters["@amt"].Value = rdr["OrderQty"];
writeCommand.Parameters["@pid"].Value = rdr["ProductID"];
writeCommand.ExecuteNonQuery();
}
}
marsConnection.Close();
正如你所見,這裡的方式在內存和數據庫連接方面比上一個示例更易於讀取和管理且更為有效。並且,在這種情況中,只讀取一次,隨後跟著的是寫操作;在某種典型情形下你的不使用MARS功能的代碼有可能更為復雜,並因此使得MARS為你帶來的節省更為明顯。
盡管MARS便利了在同一個連接上的多重活動結果集的操作,但是在這些結果集上的操作仍然是串行運行的;如果你要求並行處理數據,那麼多連接還是必需的。而且,請注意,一個使用了MARS功能的連接要比不使用的連接將利用更多些的資源。當然,從長遠來看,你卻節約了資源-由於你可以在同一個連接上執行多個命令;但是如果你在不需要的地方使用了MARS(也就是,如果你只需要單個結果集),你將會嚴重地影響系統性能。因此,如果你是在基於多數據庫連接構建一個應用程序,那麼你必須認真考慮哪些連接需要MARS,而哪些連接不需要-為了最大限度地利用資源。
總之,MARS是在ADO.NET中新增的一個非常優秀的功能-它允許你更高效地編寫應用程序。歡迎使用MARS!