470 likes | 826 Views
ADO.NET 開發高手系列. 台灣微軟資深講師 從 1993 年開始於台灣微軟主講研討會 台灣微軟最有價值專家 兩度當選 MVP 資深電腦圖書作家 擁有 60 本以上的著作 資深技術顧問. 主講人:章立民. ADO.NET 開發高手線上教學課程 第十集 如何更新資料集資料並 寫回資料來源. 兩階段更新. 第一個步驟是以新資訊來更新資料集,通常也就是新增、修改、或刪除資料集當中的資料列。 將變更從資料集寫回原始資料來源。. 於具型別或不具型別資料集的資料表中新增資料列.
E N D
ADO.NET 開發高手系列 • 台灣微軟資深講師 • 從 1993 年開始於台灣微軟主講研討會 • 台灣微軟最有價值專家 • 兩度當選 MVP • 資深電腦圖書作家 • 擁有 60 本以上的著作 • 資深技術顧問 主講人:章立民
ADO.NET 開發高手線上教學課程第十集如何更新資料集資料並寫回資料來源
兩階段更新 • 第一個步驟是以新資訊來更新資料集,通常也就是新增、修改、或刪除資料集當中的資料列。 • 將變更從資料集寫回原始資料來源。
於具型別或不具型別資料集的資料表中新增資料列於具型別或不具型別資料集的資料表中新增資料列 • 請宣告一個 DataRow型別的變數。例如:Dim newRow As DataRow • 呼叫資料表的 NewRow方法來建立一個 DataRow 物件,並將它指派給步驟 1 所宣告的變數。例如:newRow = _ myDataSet.Tables("章立民工作室").NewRow() • 使用欄位的名稱或索引(從 0 開始起算)來將資料值指派給新資料列的各個欄位。例如:newRow("姓名") = "章立民"-或-newRow(0) = "章立民" • 呼叫資料列集合的 Add方法將已指派資料值的 DataRow 物件新增至資料列集合中。例如:myDataSet.Tables("章立民工作室").Rows.Add(newRow)
於具型別資料集的資料表中新增資料列 • 在設計階段將資料表名稱公開為DataSet物件的屬性並將欄位名稱公開為DataRow物件的屬性。例如:Dim newRow As DataRow = _ ds章立民工作室1.章立民工作室.NewRow()newRow.姓名= "章立民"newRow.出生日期= #1/1/1980#ds章立民工作室1.章立民工作室.Rows.Add(newRow)
於具型別或不具型別資料集的資料表中編輯資料列於具型別或不具型別資料集的資料表中編輯資料列 • 將資料值指派給特定資料列的特定欄位。 • 請透過索引(從 0 開始起算)來取得資料列集合中特定的資料列。 • 假設「姓名」欄位是資料表的第 3 個欄位,而您想要將第 1 筆資料列之「姓名」欄位的內容修改成 “章立民” :myDataSet.Tables("章立民工作室").Rows(0)("姓名" ) = "章立民" -或-myDataSet.Tables("章立民工作室" ).Rows(0)(3) = "章立民" -或-myDataSet.Tables("章立民工作室" ).Rows(0).Item("姓名" ) = "章立民" -或-myDataSet.Tables("章立民工作室").Rows(0).Item(3) = "章立民" • 將表單上的控制項繫結至資料表的欄位,然後透過控制項來編輯欄位資料。
於具型別資料集的資料表中編輯資料列 • 在設計階段將資料表名稱公開為 DataSet物件的屬性並將欄位名稱公開為 DataRow物件的屬性。例如:ds章立民工作室1.章立民工作室(1).姓名= "章立民" ds章立民工作室1.章立民工作室(1).出生日期= #1/1/1980#
如何於資料集資料表中刪除資料列 • 呼叫所要刪除之 DataRow 物件的 Delete方法。 • Delete 方法只會將資料列標示成刪除並使其 RowState屬性成為 Deleted。 • 直到您呼叫 AcceptChanges方法時才會真正將其移除並使其 RowState屬性成為 Detached。 • 如果資料列的 RowState屬性為 Added,則只要您呼叫此資料列物件的 Delete 方法,資料列就會立刻從資料表中移除而成為 Detached狀態。 • DataRowCollection 的 Count 屬性會將被標示為刪除的資料列一併計算在內。
資料更新事件 • 具型別事件: • dataTableNameRowChanging • dataTableNameRowChanged • dataTableNameRowDeleting • dataTableNameRowDeleted • 資料更新事件: • ColumnChanging • ColumnChanged • RowChanging • RowChanged • RowDeleting • RowDeleted
暫時停止更新條件約束 • 變更資料列中的資料之前,先呼叫 DataRow物件的 BeginEdit方法。 • 開始更新這個資料列。 • 呼叫 EndEdit方法來認可對資料列的變更,然後重新啟用條件約束檢查。 • 如果需要的話,呼叫資料列的 CancelEdit方法來捨棄資料列的變更。
合併資料集(1/2) • 來源資料集中的新資料列會被加入目標資料集當中。 • 來源資料集當中的額外欄位也會被加入目標資料集當中。 • 適合的作業模式: • 擁有一個本機資料集,並且從其他應用程式或 XML Web Service 之類的元件中取得第二個資料集,而且第二個資料集內含要新增至第一個資料集的資料列。 • 您的應用程式要從兩個不同的元件取得資料集,但是需要在單一資料集中使用資料。
合併資料集(2/2) • 呼叫資料集的 Merge方法。 • 合併具有大量相似結構描述的兩個 DataSet 物件。 • 藉由將 Merge 方法的 missingSchemaAction參數設定成 MissingSchemaAction.Add來將結構描述項目加入目標資料集。 • DataRowState 屬性為 Unchanged、Modified 或 Deleted 的所有來源資料列會使用相同的主索引鍵值來對應至目標資料列。 • DataRowState 屬性為 Added 的來源資料列,則會使用與新來源資料列相同的主索引鍵值來對應至新目標資料列。 • 在合併期間,會停用條件約束。
認可資料集中的變更 • 如果您要將資料集當中的資料異動寫回資料來源,則請在成功執行資料配接器的 Update 方法之後再呼叫資料集的 AcceptChanges 方法。 • 如果您要從其他資料集複製資訊並接著認可變更,請先呼叫資料集的 Merge 方法,然後再呼叫資料集的 AcceptChanges 方法。
檢查變更的資料列 • 當變更資料集當中的資料列時,關於這些變更的資訊會被儲存,直到您認可它們為止。 • 每一筆資料列中之變更的追蹤方式: • 透過 DataRow 物件的 RowState 屬性來保有變更的類型。 • 使用資料集替變更資料列所維護的多個版本。 • 使用資料集的 HasChanges 方法來判斷資料集是否內含變更的資料列。
擷取變更的資料列 • 呼叫資料集或資料表的 GetChanges方法會傳回只包含已變更資料列的新資料集或資料表。 Dim myChangedRowsDS As DataSet myChangedRowsDS = ds.GetChanges() Dim myChangedRowsDS As DataSet myChangedRowsDS = ds.GetChanges(DataRowState.Added)
於資料集當中搜尋個別的資料列 • DataRowCollection 物件的 Find方法可以透過資料表的主索引鍵來搜尋資料列。 Dim foundRow As DataRow ' 建立一個主索引鍵值陣列來加以搜尋 Dim findTheseVals(2) As Object ' 將陣列的各個元素設定成所要搜尋的資料值 findTheseVals(0) = "章" findTheseVals(1) = "立民" findTheseVals(2) = "中和" foundRow = myTable.Rows.Find(findTheseVals) ' 顯示出所找到之資料列的第一個欄位內容 If Not (foundRow Is Nothing) Then Console.WriteLine(foundRow(1).ToString()) End If ' 搜尋主索引鍵值為 M123321000 的資料列 Dim foundRow As DataRow = myTable.Rows.Find("M123321000") ' 顯示出所找到之資料列的第一個欄位內容 If Not (foundRow Is Nothing) Then Console.WriteLine(foundRow(1).ToString()) End If
Find 方法使用注意事項 • 務必已經替資料表建立主索引鍵,否則將擲回 MissingPrimaryKeyException 例外狀況。 • 使用限制: • Find 方法不會區分大小寫。 • 不能於 Find 方法中使用萬用字元。以下陳述式表示尋找主索引鍵為 Ch% 的資料列:myDataRow = _ myDataTable.Rows.Find("Ch%")
如何取得資料列的特定版本(1/4) • 只有在編輯資料列之後以及呼叫 AcceptChanges 方法之前,資料列才會存在不同版本。 • 在 ColumnChanging 和 ColumnChanged 事件期間檢視資料列的不同版本以便進行驗證。 • 如果暫時停用條件約束,透過迴圈來循序處理 Columns 集合並比較不同的DataRowVersion值。
如何取得資料列的特定版本(3/4) Dim firstRow As DataRow = _ ds.Tables("章立民工作室").Rows(0) ' 編輯「姓名」欄位的內容 firstRow("姓名") = "章立民“ ' 取得第一筆資料列之「姓名」欄位的 Current 版本 Dim CurrentName As String = _ firstRow("姓名", DataRowVersion.Current) ' 取得第一筆資料列之「姓名」欄位的 Original 版本 Dim OriginalName As String = _ firstRow("姓名", DataRowVersion.Original)
如何取得資料列的特定版本(4/4) • 呼叫 HasVersion方法並且將 DataRowVersion 當作引數傳遞給它,以便檢測 DataRow 物件是否具有特定的資料列版本。例如:DataRow.HasVersion( _ DataRowVersion.Original)
資料集當中的資料驗證(1/2) • 透過主索引鍵與唯一條件約束來進行唯一性驗證。 • 透過外部索引鍵條件約束來確保資料參考完整性。 • 使用欄位的相關屬性來輔助完成資料驗證作業。 • 自行替應用程式撰寫資料驗證邏輯的程式碼,以便在欄位和資料列變更事件期間檢查資料。
資料集當中的資料驗證(2/2) • 資料列發生變更時將引發的 DataTable 物件的下列事件: • ColumnChanging 與 ColumnChanged 事件。 • RowChanging 與 RowChanged 事件。 • 每次變更一個欄位時將會依序引發四個事件: • ColumnChanging • ColumnChanged • RowChanging • RowChanged • BeginEdit 與 EndEdit 方法的影響。
在欄位變更期間驗證資料 Private Sub dt_ColumnChanging(ByVal sender As _ Object, ByVal e As _ System.Data.DataColumnChangeEventArgs) _ Handles dt.ColumnChanging Dim newvalue As Integer = _ CType(e.ProposedValue, Integer) If newvalue < 10000 Then MessageBox.Show(newvalue.ToString() & _ " 不能小於 10000") Throw New Exception(newvalue.ToString() & _ " 不能小於 10000") End If End Sub • 替資料表的 ColumnChanging 事件建立事件處理常式。 • 在事件處理常式中檢查 ProposedValue 和 Row 屬性來傳回建議值和原始值。例如:Dim newValue As String = CType(e.ProposedValue, String)Dim origvalue As String = CType(e.Row(e.Column), String) • 如果要取得正被變更之欄位的資訊,請檢查傳遞至事件處理常式的 Column 屬性。例如:Dim colDataType As String = e.Column.DataType.ToString()Dim colName As String = e.Column.ColumnName • 如果要拒絕變更,請擲回例外狀況。
在資料列變更期間驗證資料 • 首先,請替資料表的 RowChanging事件建立事件處理常式。 • 在事件處理常式中透過擷取各個欄位的 Proposed 版本來取得資料列的變更:newName = CType(e.Row("客戶名稱", _DataRowVersion.Proposed), String) • oldName = CType(e.Row("客戶名稱", _DataRowVersion.Original), String) • 執行驗證。
資料列變更期間驗證資料簡例 Private Sub dtCustomers_CustomersRowChanging( _ ByVal sender As System.Object, _ ByVal e As ds.CustomersRowChangeEvent) _ Handles dtCustomers.CustomersRowChanging Dim original As String = "" Dim proposed As String = "" If e.Row.HasVersion(DataRowVersion.Original) Then original = _ CType(e.Row("客戶編號", DataRowVersion.Original), String) Else original = "“ End If proposed = e.Row.客戶編號 If original <> "" Then If proposed = "" Then Throw New Exception("客戶編號不能是空的。") End If End If End Sub
將資料集變更寫回資料來源 • 必須先設定資料配接器的InsertCommand、UpdateCommand 與 DeleteCommand 屬性,然後再呼叫資料配接器的 Update 方法。 • Update 方法會循序處理資料表中的每一筆資料列,並根據資料列的 RowState 來執行適當的陳述式(也就是 INSERT、UPDATE 或 DELETE 陳述式)。 • 這些陳述式並不會被當作一個批次來執行,而是每一筆資料列個別執行其更新。 • 欲控制不同類型之陳述式的執行順序,必須先取得新增、修改、與刪除的資料列,然後依所需的順序更新之。 • UpdatedRowSource 屬性。 • OnRowUpdated 事件。
使用 Update 方法時的執行順序 • DataRow 中的值會被移至參數值。 • OnRowUpdating 事件會被引發。 • 執行命令。 • 如果命令設定為 FirstReturnedRecord,則傳回的第一個結果會置入 DataRow 中。 • 如果有輸出參數,它們會被置入 DataRow 中。 • OnRowUpdated 事件會被引發。 • AcceptChanges 方法會被呼叫。
Update 方法的使用注意事項(1/4) • 應該在 Try...Catch 區塊內呼叫資料配接器的 Update 方法。 • 如果您希望在遇到錯誤時繼續更新作業而不要產生例外狀況,請在呼叫 Update 方法之前,先將資料配接器的 ContinueUpdateOnError屬性設定成True。 • 可在資料配接器的 RowUpdated事件中,以每一筆資料列為基礎來回應錯誤。 • 可將 RowUpdatedEventArgs的 Status屬性設定成 Continue。 • 如何控制新增、修改、與刪除作業的處理順序 • 使用 DataTable 的 Select方法來傳回特定資料列狀態的 DataRow 物件陣列,並傳遞給資料配接器的 Update 方法以便只處理這些資料列。
Update 方法的使用注意事項(2/4) Dim myTable As DataTable = ds.Tables("客戶") ' 先處理刪除的資料列 myDA.Update( _ myTable.Select(Nothing, Nothing, DataViewRowState.Deleted)) ' 接著處理已更新的資料列 myDA.Update( _ myTable.Select(Nothing, Nothing, DataViewRowState.ModifiedCurrent)) ' 最後再處理已新增的資料列 myDA.Update( _ myTable.Select(Nothing, Nothing, DataViewRowState.Added))
Update 方法的使用注意事項(3/4) • 必須分別呼叫每一個資料表所對應之資料配接器的 Update 方法來個別更新它們。 • 如果資料表之間具有關聯性連結: • 子資料表:刪除資料列。 • 父資料表:新增、修改、與刪除資料列。 • 子資料表:新增與修改資料列。 • 為什麼要在更新資料來源之後去重新填入資料集? • 反應出其他使用者的變更。 • 擷取由資料庫所計算的欄位值。 • 為了能夠在並行控制中使用時間戳記。
Update 方法的使用注意事項(4/4) • 當資料來源的資料表擁有自動編號欄位時 • 在 DataSet 內建立 AutoIncrementStep 為 -1以及 AutoIncrementSeed 為 0的攔位。 • 請確定資料來源所產生的自動遞增值從 1開始,並以正值來遞增。 • 可以使用 Guid 型別的欄位來取代自動遞增欄位。
如何回應資料庫更新錯誤 • 替 RowUpdated 事件建立事件處理常式。 • 檢查 RowUpdated 事件的 Status 屬性。如果有錯誤,此值將會是 ErrorsOccurred。 • 如果您要控制更新是否繼續,請將 Status 屬性設定為 UpdateStatus 列舉型別的某一個成員。 • 您可以視需要在事件物件中設定目前資料列的 RowError 屬性。
ADO.NET 的並行控制 • 「並行控制」(Concurrency Control)是用來辨識和解決當有多位使用者同時更新相同的資料時所衍生的相關問題,以便確保資料的一致性。 。
並行控制的類型 • 封閉式並行控制(Pessimistic Concurrency Control) • 適用強況: • 相同資料列被爭用的機率非常高時… • 不允許在交易期間變更資料… • 中斷連接的架構無法使用封閉式並行控制。 • 「樂觀並行控制」(Optimistic Concurrency Control) • 已變更之資料列的原始版本會與資料庫中的現有資料列比較,以便檢查資料庫中的資料列是否已被其他使用者變更過,如果發現兩者有所不同,則更新就會失敗並發生並行錯誤。 • 「後進先寫入」(Last In Wins)
ADO.NET 與 VS.NET 的並行控制 • 採用樂觀並行控制。 • 必須自行撰寫程式邏輯來解決並行違規的問題。 • 判斷是否發生變更: • 版本編號方法。 • 儲存所有值方法。
版本編號方法 • 要被更新的資料列必須擁有一個內含日期時間戳記或版本編號的欄位。 • 時間戳記相符時才更新:UPDATE myTable SET Column1 = @newvalue1, Column2 = @newvalue2 WHERE DateTimeStamp = @origDateTimeStamp • 版本編號相符時才更新:UPDATE myTable SET Column1 = @newvalue1, Column2 = @newvalue2 WHERE RowVersion = @origRowVersionValue
儲存所有值方法(1/2) • 讀取資料列時取得所有欄位的複本。 • DataSet 物件會維護每一個被修改之資料列的兩個版本:一為原始版本(原本從資料來源讀取的版本),另一個則是使用者更新過的修改版本。 • 當嚐試將資料列寫回資料來源時,會將資料列中的原始值與資料來源中的資料列加以比較。如果兩者相符,成功寫回。
儲存所有值方法(2/2) UPDATE 客戶 SET 客戶編號 = @currCustomerID, 公司名稱 = @currCompanyName, 連絡人 = @currContactName, 連絡人職稱 = @currContactTitle, 地址 = @currAddress, 城市 = @currCity, 郵遞區號 = @currPostalCode, 電話 = @currPhone, 傳真電話 = @currFax WHERE (客戶編號 = @origCustomerID) AND (地址= @origAddress OR @origAddress IS NULL AND 地址 IS NULL) AND (城市 = @origCity OR @origCity IS NULL AND 城市 IS NULL) AND (公司名稱= @origCompanyName OR @origCompanyName IS NULL AND 公司名稱 IS NULL) AND (連絡人 = @origContactName OR @origContactName IS NULL AND 連絡人IS NULL) AND (連絡人職稱 = @origContactTitle OR @origContactTitle IS NULL AND 連絡人職稱 IS NULL) AND (傳真電話 = @origFax OR @origFax IS NULL AND傳真電話 IS NULL) AND (電話 = @origPhone OR @origPhone IS NULL AND 電話 IS NULL) AND (郵遞區號 = @origPostalCode OR @origPostalCode IS NULL AND 郵遞區號 IS NULL); SELECT 客戶編號, 公司名稱, 連絡人, 連絡人職稱, 地址, 城市, 郵遞區號, 電話, 傳真電話 FROM 客戶 WHERE (客戶編號 = @currCustomerID)
如何處理並行錯誤 • 使用 DBConcurrencyException 物件來解決並行違規所引發的問題。 • 如果受影響的資料列數目等於零則由 DataAdapter 在更新作業期間擲回的例外狀況(Exception)。
並行違規範例 • DemoForm4.vb • 展示如何將資料集當中的變更寫回資料來源。 • 具備完善的機制來處置並行違規的狀況。
結束 • 別忘了定期上下列網站: • MSDN 中文網站http://www.microsoft.com/taiwan/msdn/ • 微軟技術社群網站http://www.microsoft.com/taiwan/community • 微軟最有價值專家 MVPMicrosoft Most Valuable Professionals • 微軟爲什麼要選拔 MVP • 誰可以申請為微軟最有價值專家(MVP)