http://big5.webasp.net/article/17/16467.htm
最近幾天一直在圖書館裡面看《ADO.NET實用指南》,發現真是一本好書。讀書自然就有心得,本人就根據書的線索,把自己的學習體會主要以代碼的形式記錄下來。(書上對應代碼在http://www.adoguy.com/book裡)
1、連接
ADO.NET最大的特色就在於支持在斷開連接的情況下對數據庫裡的內容進行操作,這樣可以大大的節約過多連接帶來的消耗,前面的那一篇文章中已經給了一 個具體的例子說明ADO.NET的這種特性。我們可以在從數據庫裡獲得數據的時候打開連接,在得到數據之後就斷開連接,對dataset裡面的數據進行操 作,然後在把dataset裡的內容更新到數據庫裡面的時候再打開連接。對於dataReader則必須一直保持連接。
使用這種特性的時候有幾點要注意一下:
(1)更改連接屬性的時候必須斷開連接
(2)切換數據庫的時候選擇conn.changeDatabase(dbName),減少斷開連接與新建連接往返帶來的消耗
ADO.NET同時支持數據庫自帶連接池。在一個連接關閉之後,連接會在池中保持一段時間,然後才實際的關閉,如果在超時之前,有人請求建立相同的連接, 就將打開的連接分配給請求者,這對於經常進行打開和斷開的連接可以減少很多的消耗。不過在SQL SERVER 2000中採用集成安全性的連接無法入池。
連接涉及到的事件有Dispose,InfoMessage,StateChange,在MSDN可以查到,不再贅述。
模板代碼:
Dim conn As SqlConnection
conn=New SqlConnection("……") '裡面為連接字符串
conn.open()
'進行相應的操作
conn.close()
2、Command對像
ADO.NET允許以三種不同的方式獲取數據庫裡面的數據Command,DataSet,DataReader,Command是最基本的方法,通過執行SQL命令的形式獲得數據。
(1)創建 可以用兩種方式創建
a、創建新的Command對像
Dim cmd As New SqlCommand
cmd.connection=conn
cmd.CommandText="SELECT * FROM Customer"
b、獲得對conn中command對象的引用
Dim cmd As SqlCommand
cmd=conn.createCommand();
cmd.CommandText="SELECT * FROM Customer"
推薦第二種方法
(2)執行 四種執行方式
ExecuteNonQuery() 返回受命令影響的行數
ExecuteScalar() 返回第一行第一列(使用與集函數)
ExecuteReader() 返回一個DataReader對像
ExecuteXmlReader()返回一個XmlReader對像
(3)參數 主要是用在存儲過程中,有複雜和精簡兩種形式
複雜方法:
Dim param As new SqlParameter("@Return",SqlDbType.Int)
param.Direction=ParameterDirection.ReturnValue
cmd.Parameters.Add(param)
精簡方法
cmd.Parameters.Add("@Return_value",DbType.Int32).Direction=ParameterDirection.ReturnValue
建議:如果需要處理輸出值時的時候使用參數,只處理輸入值的時候就不用使用參數了。
(4)事務
用SQL語句:
Begin TRAN
SQL操作
If @@ERROR <>0
Begin
RollBack TRAN
Return @@ERROR
End
Commit TRAN
Return 0
在ADO.NET中編寫事務
cmd.Transaction = conn.BeginTransaction()
try
{
cmd.CommandText="..."
cmd.ExecuteNonQuery()
cmd.Transaction.commit()
}
catch(Exception ex)
cmd.Transaction.Rollback()
End try
如果希望將數據庫事務處理與一些外部系統結合起來(比如在數據庫更新時同時進行了WEB更新,如果WEB更新失敗希望回滾事務時),選擇客戶端編寫事務處理(用ADO.NET編寫)
僅僅做數據庫事務處理的話就直接在服務端寫事務語句(在SQL SERVER2000 中寫事務)
在事務中可以創建SavePoint來實現部分事務回滾
cmd.Transaction.save("New Customer")
cmd.Transaction.Rollback("New Customer")
(5)批處理查詢 如果有多條SQL語句並且可以一起執行的話,就可以進行批處理查詢。在DataReader中支持批處理查詢獲得的數據集。
cmd.CommandText="SELECT * FROM Customer;SELECT * FROM Inovince;"
Dim rdr As SqlDataReader
rdr=cmd.ExecuteReader()
rdr中即包含兩個SQL語句執行的結果
3、DataReader對像
DataReader對像只能對查詢獲得的數據集進行自上而下的訪問,但效率很高。如果僅僅是訪問數據的話,可以使用DataReader。但 DataReader要求一直連接,所以將結果的一小部分先放在內存中,讀完後再從數據庫中讀取一部分,相當於一個緩存機制。這對於查詢結果百萬級的情況 來說,帶來的好處是顯而易見的。
模板代碼:
Do While rdr.Read()
Console.WriteLine(rdr(0)) '也可輸出rdr("CustomerID")
Loop
如果要進行類型限制的話可以輸出(String)rdr(0)或rdr.GetString(0)
從DataReader裡讀出數據的時候要注意屬性是否為空,如果屬性可以為空,則讀出數據時應進行判斷
If Not rdr.IsDBNull(0)
Console.writeLine(...)
用DataReader讀取記錄時,數據庫默認上鎖,可以通過更改DataReader的默認屬性改變。
如果DataReader裡面的數據是批處理語句執行得到的話,可以通過NextResult訪問
模板代碼:
Do
Do While rdr.Read()
Console.WriteLine(rdr(0))
Loop
Loop While rdr.NextResult()
處理元數據(顯示每個屬性的情況)
Dim schema As DataTable
schema=rdr.GetSchemaTable() '得到元數據表,表裡的每列對應每個屬性的特性集合
對於每一列屬性對應的row,通過row("DataType")獲得這列屬性的數據類型。
4、DataSet
在ADO.NET中DataSet的作用是為數據源提供一個斷開式的存儲,而不必關心數據源,操作只用在DataSet中進行就行了。
有三種方法可以創建DataSet:1、通過DataAdapter 2、通過XML 文件 3、用人工方法確定架構,然後逐行輸入數據。
主要介紹第一種方法。
DataAdapter用於將DataSet連接到基本數據存儲,本質上是一種元Command對象。
它包括SelectCommand對象,InsertCommand對象,UpdateCommand對象,DeleteCommand對象。
模板代碼:
Dim dataAdpater As New SqlDataAdapter("Select * From Student",conn)
Dim dataSet As New DataSet()
dataAdapter.Fill(dataSet)
這時dataSet的表名默認為Table
如果使用批處理查詢並將得到的結果填入dataSet中則表名默認為Table,Table1,Table2……
TableMappings:
表名映射:
生成dataAdapter之後再進行表名映射
dataAdapter.TableMappings.Add("Table","Customer")
dataAdapter.Fill(dataSet)
這時Table的別名就變為Customer(對dataSet用Table或Customer操作都可以),dataSet.Tables("Customer")就可以引用到這個表
或
dataAdapter.TableMappings.Add("ADONET","Customer")
dataAdapter.Fill(dataSet,"ADONET")
列名映射:
dataAdapter.TableMappings.Add("Table","Customer")
dataAdapter.TableMappings("Customer").ColumnMappings.Add("CustomerID","ID)
dataAdapter.FIll(dataSet,"Customer")
架構(Schema),通過FillSchema添加架構
1、添加主鍵
customerTable.PrimaryKey=New DataColumn[]{CustomerTable.Columns("CustomerID")} (通過數組的形式添加主鍵)
2、添加關係
dataSet.Relations.Add("Customers_Invoices",dataSet.Tables("Customers").Columns("CustomerID"),dataSet.Tables ("Invoinces").Columns("CustomerID"),true)
3、添加約束
有兩種主要約束:唯一約束、外碼約束(UniqueConstraint,ForeignKeyConstraint)
其中唯一約束又分為DeleteRule(級聯刪除約束)、UpdateRule(級聯更新約束)、AcceptRejectRule(調用AcceptChanges或 RejectChanges時的約束)
4、添加觸發器
可以對6中DataSet事件添加觸發器
RowChanging,RowChanged,ColumnChanging,ColumnChanged,RowDeleting,RowDeleted
5、列架構
比如添加列的屬性:customerTable.Columns("CustomerID").ReadOnly=true
或添加AutoIncrement列:
customerTable.Columns("CustomerID").AutoIncrement=true
customerTable.Columns("CustomerID").AutoIncrementSeed=1 (列起始位置)
customerTable.Columns("CustomerID").AutoIncrementStep=1 (列遞增步長)
可能有人會說這一切在DBMS裡面做不就行了嗎,幹嗎那麼大費周折的在ADO.NET對Dataset再寫一遍呢?
這主要是出於對效率方面的考慮,如果客戶端的錯誤輸入能在客戶端就被發現出來,而不用傳到服務端進行驗證的話就可以減少不必要的傳輸了。
表達式列:
Dim exColumn As New DataColumn("LineTotal")
exColumn.DataType=typeof(float)
exColumn.Expression="((price-(price*Discount))*Quantity)"
dataSet.Tables("items").Columns.Add(excolumn)
(由於書上的代碼全是用C#寫的,轉換為VB.NET實在麻煩,以後就不轉換了,見諒)
五、操縱dataset
在DataSet中DataRow是其所有數據的基本存放位置,它主要是由一個值數組組成,代表DataTable單獨一行。
DataRow中主要包括一下幾種信息:1、行中每一列的當前值,2、行中每一列的原始值,3、行狀態,4、父行與子行間的鏈接
初始化一個DataRow:
DataTable dataTable=dataSet.Tables[0];
DataRow newRow=dataTable.NewRow(); //用dataTable生成DataRow可以利用dataTable裡面的模式
dataTable.Rows.Add(newRow);
刪除行:
DataTable.Rows.Remove(行實例);
DataTable.Rows.RemoveAt(行號);
DataRow.Delete(); //行自身移除
讀寫DataRow的值:
row["列名"],row[列號]均可引用其中的一個屬性
DataColumn a=dataTable.Columns("列名"); //可以獲得一個列
對行進行批處理更改:
BeginEdit()開始更改,EndEdit()結束更改,同時將更改結果寫入DataSet,CancelEdit(),取消更改
例如:
row.BeginEdit();
對row進行更改
row.EndEdit();
將數據批量加載到DataTable
dataTable.BeginLoadData();
dataTable.LoadDataRow(row1,false); //第二個參數為true時,調用dataTable.AcceptChanges()時接受更改,為false直接添加
……
dataTable.EndLoadData();
使用這種數據加載方式可以在數據加載期間屏蔽所有的數據約束,索引也不會予以維護,極大的加快了數據加載速度
行的版本:
current:當前值
default:根據操作的不同決定行的default值
original:最後一次調用AcceptChanges()之後的值
proposed:調用AcceptChanges()之前被更改的值
例如要獲得行的original值:
String oldString=row("FirstName",DataRowVersion.original);
行的狀態:
row.RowState獲得行的狀態,例如刪除後變成Deleted,數據存儲更新後變為unchanged
六、DataSet導航
在ADO.NET中每個表都保持其相對獨立性,允許在行級上導航不同表之間的相關行(向下導航到子行,向上導航的父行)
如DataRow[] invoiceRows=custRow.GetChildRows("Customer_invoice"); //通過關係導航到子行
七、DataView
DataView就時數據視圖,為數據庫結構提供了外模式的實現。
同時DataView也可以為窗體控件和Web控件提供數據綁定功能,在每一個DataTable中內建了一個DataView為:DataTable.DefaultView();
創建DataView
DataView sortedView=new DataView(dataTable);
對DataView進行排序
dataTable.DefaultView.sort="lastName";
dataTable.DefaultView.sort="lastName,FirstName DESC";
對DataView進行篩選:
1、通過對其中的RowFilter屬性設置可以實現篩選
dataTable.DefaultView.RowFilter="Vendor='Rawlings'";
不過篩選表達式只能設置成比較簡單的表達式,功能有限,不過可以滿足基本的要求。
同樣在DataTable裡面也可以進行簡單的搜索,返回一個DataRow數組,例:
DataRow[] compoundRows=dataTable.select("Vendor='wilson' AND price>20.00)
2、通過RowState來篩選
dataTable.DefaultView.RowStateFilter="DataViewRowState.originalRows"可以篩選出符合要求狀態的row
對DataView進行搜索:
相對於DataView使用RowFilter進行篩選得到一個矩形數據集,使用Find、FindRows可以更準確的查找到與特定鍵相匹配的行
搜索的時候必須首先設置DataView的sort屬性:
int found=dataTable.DefaultView.Find("wilson"); //獲得行的位置
DataRowView[] rows=dataTable.DefaultView.FindRows("Rawlings") //過得一個row數組
八、更新DB
在DataSet中,每一個DataTable對應著一個DataAdapter,DataAdapter.Update()時,DataTable自動更新。
更新的時候可以使用CommandBuilder自動根據DataSet的變化生成更新的SQL命令
SqlCommandBuilder bldr=new SqlCommandBuilder(dataAdapter);
dataAdapter.Update(custTable);
不過Update接受DataSet參數並不更新DataSet而是更新DataSet中的一個叫"Table"的表
使用CommandBuilder進行更新的時候要注意一下幾點:
1、SelectCommand必須有效
2、必須有主碼
3、若SelectCommand填充DataTable後架構發生改變,應該在Update()之前調用CommandBuilder.RefreshSchema();
4、更新DB時不受關係、約束或者DataSet中其他表的影響
雖然使用CommandBuilder比較方便,不用自己寫更新命令,但自動生成的命令性能不高,這時可以考慮自己編寫存儲過程或直接使用帶參數的sql語句,例如:
String insQry="Insert into Customer(CustomerID) Values (@Customer)";
SqlCommand insCmd=conn.CreateCommand();
insCmd.CommandText=insQry;
SqlParameterCollection insParams=insCmd.Parameters;
insParams.Add("@CustomerID",SqlDbType.UniqueIdentifier,0,"CustomerID");
dataAdapter.InsertCommand=insCmd;
dataAdapter.Update();
在dataAdapter.Update()更新時還可以控制更新的範圍:
dataAdapter.Update(invTable.GetChanges(DataRowState.Deleted); //只更新被刪除的部分
九、事務
tx=conn.BeginTransaction(IsolationLevel.Serializable);
invDA.SelectCommand.Transaction=tx;
事務操作
tx.Commit();提交 //tx.Rollback();事務回滾
十、數據綁定
簡單版本:(對文本框、標籤等)
{Controls}.DataBindings.Add("{Property}",{dataSource},"{dataMember}");
其中Property為待綁定的屬性,dataSource為DataView或DataTable,dataMember為dataSource其中的某個屬性
複雜版本:(對ListBox、ComboBox等)
要分別設置各個屬性實現綁定:
DataSource=支持IList的一個對像(DataTable或DataView)
DisplayMember:待顯示的DataSource中的一個屬性
ValueMember:確定在DataSource中引用哪一個數據行,即實現與DisplayMember的名值對應
DataGrid綁定:
1、可以設置DataSource屬性實現靜態綁定
2、可以使用SetDataBinding函數實現動態綁定
同時DataGrid支持主控/詳細表顯示
masterGrid.setDataBinding(customerTable,"");
detailGrid.setDataBinding(customerTable,"Customer_Invoices") //第二個屬性要設置成關係約束
這樣在主表中選擇一行,在子表中就根據主表行中外碼在子表中找到相應行
綁定之後,綁定項中就有一個CurrencyManager屬性實現游標功能
BindingContext[CustomerTable]返回一個CurrencyManager對象,其中的Position屬性可以更改,實現游標的移動。