• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
    • 軟件測試技術
    • 軟件測試博客
    • 軟件測試視頻
    • 開源軟件測試技術
    • 軟件測試論壇
    • 軟件測試沙龍
    • 軟件測試資料下載
    • 軟件測試雜志
    • 軟件測試人才招聘
      暫時沒有公告

    字號: | 推薦給好友 上一篇 | 下一篇

    DataSet的數據并發異常處理

    發布: 2007-6-11 12:47 | 作者: 網絡轉載 | 來源: 網絡 | 查看: 62次 | 進入軟件測試論壇討論

    領測軟件測試網
      摘要:ADO.NET為提高數據密集型(data-intensive)應用程序的性能、簡化這類程序的建立過程提供了多種技術。數據集(DataSet)作為ADO.NET對象模型的標志,作為一個微型的、不連接(disconnected)的數據源的副本提供服務。雖然使用數據集通過減少對數據庫服務器的高花費的訪問而提高了性能,但是它也帶來了多個用戶試圖同時訪問相同數據的可能性,由此引起數據并發性異常(data concurrency exception)。本文調查了數據并發性異常背后的通常起因,介紹了解決這些問題的技術。
     
      把數據訪問層升級到ADO.NET有很多好處,其中之一是使用內部數據集對象。數據集對象基本上是一個不連接的、內存中的數據庫的拷貝。數據集對象包含一個或者多個數據表(DataTable),每個數據表一般對應于數據庫中的一個表。數據集提供了很多好處,但也帶來一些問題,特別是可能遇到與數據并發性異常相關的問題。我建立了一個簡單的Windows Forms顧客服務應用程序,用它來解釋該問題的潛在的缺陷。本文我將介紹該應用程序并演示怎樣解決它所引起的數據并發性問題。

      本文建立的顧客服務應用程序示例是使用Visual Basic .NET和SQL Server 2000建立的,但是由于微軟.NET框架組件是語言無關(language-agnostic)的,因此任何與.NET框架組件兼容的語言都可以使用。同樣,由于數據集對象抽象了數據源,數據源的實際執行并不重要;無論下層的數據源是SQL Server、本地XML文件或者從一個服務中檢索到的數據,數據并發性異常同樣會出現。

      數據集的利弊

      數據集提供了很多好處,例如比起數據庫層次,它強化了內存中的完整性規則。數據集對象可以定義和強化表之間的關系和列的約束,確保使用的商業規則對數據庫沒有缺陷。通過數據庫抽象,你能建立單個代碼集合訪問數據集對象而不必考慮填充該數據集的源數據。下層的數據源也許是SQL Server、Oracle甚至XML文件。無論下層數據源是什么,代碼使用相同的方法與數據集交互。這使你能改變下層數據源而不改變代碼。

      但是使用數據集的最大好處是提高了性能。因為數據集與下層數據庫斷開,代碼將更少作數據庫的調用,顯著地提高了性能。你能向數據集的多個數據表中添加新行、驗證每行的有效性和參照完整性。數據適配器(DataAdapter)把數據集連接到下層數據庫,能使用一條命令更新下層數據庫。每個表中的所有新行都使用命令加入,以確保所有添加到數據庫的行都是有效的。

      性能的最優化是有代價的。因為數據集對象與下層數據庫斷開,經常有機會出現數據沒有超期(out of date)的情況。因為數據集不保存活動數據,只保存當時填充數據集的活動數據的一個快照,與數據并發性相關的問題就會出現。數據并發性問題出現在多個用戶訪問相同的數據并且任何一個用戶沒有其它用戶的信息就能更新數據。這就出現了一個用戶偶然更新數據而不知道那些數據已經改變了,不是他在程序中看到的了。幸運的是數據集對象擁有捕獲數據并發性問題的內建(built-in)支持,因此應用程序能正確地作出反應。

    示例程序

      一個虛擬的公司使用該顧客服務應用程序建立顧客訂單,更新顧客的個人信息。有很多客戶銷售代表(CSR)在桌面上使用該應用程序。CSR使用電話獲取訂單,從顧客那兒收集個人信息和支付信息。顧客記錄保存在數據庫中以提高回頭客處理訂單的速度,CSR接著建立一個訂單并把產品項添加上去,指定數量和目前的價格,所有的信息收集后,CSR點擊Place Order按鈕,向數據庫中插入顧客和訂單記錄。

      CSR也使用應用程序執行通過電子或者緩慢的郵件發送給公司的請求。這些請求在CSR間均勻分開,在每天早晨發送給他們,CSR通過電話執行那些請求。系統設計要提高請求的實現速度,所有的顧客在CSR之間共享。顧客的每個請求,無論通過電話或者郵件,都被不同的CSR處理,增加了發生數據并發性問題的機會。
    為了提高性能,應用程序在內存中保持了一個用顧客和訂單信息填充的數據集對象。因為很多雇員同時使用該應用程序,就會有許多活動數據的不連接的快照,它們都在雇員的工作站上。所有顧客的維護、訂單輸入和訂單維護都使用名為dsAllData的數據集對象。圖1是建立dsAllData的代碼,它是全局模塊的一部分,因此應用程序中的所有窗體都能使用它。

    Const connString = server=localhost;database=northwind;uid=sa;pwd=
    Public connCustSvc As SqlClient.SqlConnection
    Public daCustomer As SqlClient.SqlDataAdapter
    Public cbCustomer As SqlClient.SqlCommandBuilder
    Public daOrders As SqlClient.SqlDataAdapter
    Public cbOrders As SqlClient.SqlCommandBuilder
    Public daOrderDetail As SqlClient.SqlDataAdapter
    Public cbOrderDetail As SqlClient.SqlCommandBuilder
    Public dsAllData As DataSet
    Public Sub Main()
     connCustSvc = New SqlClient.SqlConnection(connString)
     daCustomer = New SqlClient.SqlDataAdapter(SELECT * FROM Customer, connCustSvc)
     cbCustomer = New SqlClient.SqlCommandBuilder(daCustomer)
     daOrders = New SqlClient.SqlDataAdapter(SELECT * FROM Orders, connCustSvc)
     cbOrders = New SqlClient.SqlCommandBuilder(daOrders)
     daOrderDetail = New SqlClient.SqlDataAdapter(SELECT * FROM OrderDetail, connCustSvc)
     cbOrderDetail = New SqlClient.SqlCommandBuilder(daOrderDetail)
     dsAllData = New DataSet()
     daCustomer.MissingSchemaAction = MissingSchemaAction.AddWithKey
     daCustomer.Fill(dsAllData, Customer)
     daOrders.MissingSchemaAction = MissingSchemaAction.AddWithKey
     daOrders.Fill(dsAllData, Orders)
     dsAllData.Tables(Orders).Columns(Total).DefaultValue = 0
     daOrderDetail.MissingSchemaAction = MissingSchemaAction.AddWithKey
     daOrderDetail.Fill(dsAllData, OrderDetail)
     Application.Run(New frmCustomerMaintenance())
    End Sub

    圖1.填充數據集對象

      建立dsAllData的代碼建立了一個空的數據集對象、三個數據適配器(DataAdapter)和三個命令構造器(CommandBuilder)。每個數據適配器在適當的表上執行一個簡單的SELECT *操作,而命令構造器用需要的剩余信息填充數據集,使它有插入(insert)、更新(update)和刪除(delete)的能力。主程序使用數據適配器對象和所有三個表中的數據填充dsAllData,接著使用Customer Maintenance窗體開始應用程序。
    圖2顯示的是Customer Maintenance屏幕,它有一個綁定到dsAllData的Customers數據表的DataGrid對象。這個簡單的表格允許CSR編輯顧客的任意基本屬性。因為該表格綁定到了Customers數據表,表格中的任何改變都將自動存儲到數據表中。dsAllData將保存這些值,直到CSR點擊Save Changes按鈕明確地告訴窗體更新下層數據源為止。


    圖2. Customer Maintenance表格允許編輯

      為了輸入訂單,使用圖3中的代碼建立了幾個新行并添加到dsAllData中。首先建立一個Order記錄,接著在數據表OrderDetail中為訂單的每個項建立幾個記錄。當所有必須的行添加到dsAllData后,一個適當的數據適配器的Update方法調用將用新行更新下層數據源。

    Private Sub CreateOrder()
     Dim dr As DataRow
     dr = dsAllData.Tables(Orders).NewRow
     With dr
      .Item(DateOrdered) = Now
      .Item(CustomerID) = 1
      .Item(ShipToAddress) = 123 Main
      .Item(ShipToCity) = Kansas City
      .Item(ShipToState) = MO
      .Item(ShipToZip) = 12345
     End With
     dsAllData.Tables(Orders).Rows.Add(dr)
     AddOrderDetail(dr.Item(ID), 1, 1, 9.99)
     AddOrderDetail(dr.Item(ID), 2, 2, 4.99)
     daOrders.Update(dsAllData.Tables(Orders))
     daOrderDetail.Update(dsAllData.Tables(OrderDetail))
    End Sub

    Private Sub AddOrderDetail(ByVal OrderID As Integer, _
    ByVal ProductID As Integer, ByVal Quantity As Integer, _
    ByVal Price As Single)

     Dim dr As DataRow
     dr = dsAllData.Tables(OrderDetail).NewRow
     With dr
      .Item(OrderID) = OrderID
      .Item(ProductID) = ProductID
      .Item(Quantity) = Quantity
      .Item(Price) = Price
     End With
     dsAllData.Tables(OrderDetail).Rows.Add(dr)
    End Sub

    圖3.有細節記錄的新訂單

      因為CSR同時使用應用程序和更新顧客信息,因此好像任何時候一個CSR看到的都是過期的信息。為了防止這種現象,應用程序的設計師決定dsAllData的數據緩存要每隔30分鐘刷新一次,以確保CSR通?吹秸_信息。應用程序設計得很仔細,以確保數據刷新只在空閑時段進行,這樣它才不影響性能。數據集刷新因此會比30分鐘長一點,這依賴于CSR的行為。


    圖4.應用程序數據模型

      該應用程序的數據模型很簡單(圖4)。它使用SQL Server 2000存儲,只包含三個表,顧客表、訂單表、每個訂單的細節表,并定義了適當的主鍵和關系以確保參照的完整性。此外,在OrderDetail上定義了一個觸發器來更新Orders 表的Total列。每次插入、更新或者刪除一個OrderDetail記錄,調用觸發器計算該訂單的最后銷售值,并更新Orders表的適當的行。圖5是trg_UpdateOrderTotal觸發器的代碼:

    CREATE TRIGGER trg_UpdateOrderTotal ON [dbo].[OrderDetail]
    FOR INSERT, UPDATE, DELETE
    AS
     DECLARE @OrderID int
     SELECT @OrderID=OrderID FROM Inserted
     IF @OrderID IS NULL
     SELECT @OrderID=OrderID FROM Deleted
     UPDATE Orders
     SET Total=
     (
      SELECT Sum(Price*Quantity)
      FROM OrderDetail
      WHERE OrderID=@OrderID
     )
     WHERE ID=@OrderID

    圖5.更新Total列

      第一個數據并發性異常

      該顧客服務應用程序使用了幾個月沒有任何問題,但是突然產生了一個沒有處理的異常DBConcurrencyException。本段我將解釋導致該異常的環境。

      第一個使用該應用程序的顧客銷售服務人員Joe打開應用程序。這將初始化將數據載入數據集dsAllData并按每30分鐘一次的周期來刷新數據。Joe的收件箱中有一堆文件,包括顧客傳真、郵寄或者通過電子郵件發送的更改請求。他開始處理更改請求,但是經常被電話訂單中斷。

      其間,第二個客戶銷售服務人員Sally到達辦公室并打開了她的應用程序實例。Sally的應用程序實例也初始化從SQL Server中載數據,包括Joe所作的更新。Sally也接到了一個顧客改變電話號碼的請求。該顧客先前用電子郵件發送了地址的改變情況,但是在那時不知道他的新電話號碼,現在要更新記錄了。Sally使用Customer Maintenance屏幕更新顧客的電話號碼。當Sally改變DataGrid中的電話號碼時,新號碼存儲在dsAllData中。當Sally確認其它的顧客信息后,她意識到原地址的改變還沒有處理,因此她更新那些信息并點擊Save Changes按鈕,將新數據發送到SQL Server數據庫。

      Joe正在處理相同顧客的原始請求。當他打開Customer Maintenance屏幕時,應用程序從緩存數據集對象中讀入信息。因為Sally更新顧客地址時,Joe的應用程序沒有自動與數據庫同步,因此他的Customer Maintenance屏幕仍然顯示舊地址。Joe使用電子郵件提供的新信息改正了DataGrid中顯示的信息,并點擊Save Changes按鈕。這樣操作后出現了一個錯誤信息并發性故障:更新命令影響了0個記錄(Concurrency violation: the UpdateCommand affected 0 records),應用程序崩潰了。在Joe再次打開應用程序時,他發現地址已經更新了,認為他的更改在應用程序崩潰前已經完成了。下面就是問題的代碼行:

    Private Sub butSave_Click (ByVal sender As System.Object, _
    ByVal e As System.EventArgs)
     daCustomer.Update(dsAllData.Tables(Customer))
    End Sub

      實際的異常是DBConcurrencyException類型產生的,它是數據適配器對象內部建立的特定功能的結果(見圖6)。該數據適配器設計為把數據填充到不連接的對象(例如數據集),這樣它在執行更新前能自動地檢查數據尋找改變。如果下層數據被改變了,數據適配器將引發一個DBConcurrencyException異常而不是執行更新。


    圖6. DBConcurrency異常

      執行完整性檢查是相當直接的,它提高了數據表對象的性能,使它能夠保持多個數據集合。當數據第一次載入數據表時,數據表中的所有數據行(DataRow)的DataRowVersion屬性設置為原始的(Original)。當修改了數據行中的一列時,該行就被復制一份并標記為當前的(Current),標記為原始的行仍然沒有改變。后來的所有對該數據的更改都僅僅影響當前行。當為一個數據表(或者一個數據集中的多個數據表)執行數據適配器的更新方法時,它重復所有的當前行來決定要發送給下層數據源的更新語句。作為DataRowVersion屬性的補充,數據行有一個用于識別行中數據狀態的RowState屬性。它的可能值為Unchanged、Added、Modified、Deleted和Detached。

      在決定下層數據中的哪些行需要更新后,數據適配器dsCustomer建立更新SQL Server數據庫所需要的SQL語句。在圖1中我使用數據集和命令構造器對象來建立需要的INSERT、 UPDATE和DELETE語句。命令構造器對象建立的UPDATE語句使用DataRowVersion值為Original的數據行副本來識別和更新數據庫中的適當行。這就是說,作為使用主鍵值簡單地識別正確行的代替,命令構造器建立一個SQL語句來查找與數據集中存儲的原始值準確匹配的行。下面的代碼是建立的用于更新顧客電話號碼的UPDATE語句示例:

    UPDATE Customer SET Phone = @p1
    WHERE ((ID = @p2) AND ((FirstName IS NULL AND @p3 IS NULL)
    OR (FirstName = @p4))
    AND ((LastName IS NULL AND @p5 IS NULL) OR (LastName = @p6))
    AND ((Address IS NULL AND @p7 IS NULL) OR (Address = @p8))
    AND ((Address2 IS NULL AND @p9 IS NULL) OR (Address2 = @p10))
    AND ((City IS NULL AND @p11 IS NULL) OR (City = @p12))
    AND ((State IS NULL AND @p13 IS NULL) OR (State = @p14))
    AND ((Zip IS NULL AND @p15 IS NULL) OR (Zip = @p16))
    AND ((Phone IS NULL AND @p17 IS NULL) OR (Phone = @p18)))

      該UPDATE語句使用參數而不是實際值,但是你能看到行中每列是怎樣檢查的。

      識別了準確的行和下層數據庫中的原始值后,數據適配器就可以安全地更新行了。但是,如果自從數據表被填充后數據庫中某行的某個列改變了,UPDATE語句將失敗,因為數據庫中沒有與WHERE條件中的標準匹配的行了。數據適配器決定UPDATE是否成功很簡單,只需要簡單地檢查數據庫中被更新的行的實際數量。如果沒有行被更新,那么下層數據一定被改變或刪除了,就產生一個數據并發性異常。這就解釋了Joe試圖更新顧客電話號碼時接收到的有點模糊的錯誤消息:數據適配器檢查到的實際錯誤不是下層數據改變了,而是沒有記錄被更新,標志著下層數據必定被改變了。

      解決方法

      有兩種途徑解決DBConcurrencyException問題。第一種是確保它永不重現:我可以刪除圖1中代碼使用的SqlCommandBuilder對象,把它們更換為數據適配器對象的UpdateCommand 屬性的SqlCommand對象。我將在CommandText屬性中建立帶有WHERE條件的SQL語句,只進行主鍵而不是所有列的過慮。這樣將排除所有并發性問題(假定主鍵不會改變)。

      但是這種技術帶來了幾個問題。首先,很明顯要更多的代碼,因為我還得為每個數據適配器的InsertCommand和 DeleteCommand屬性建立SqlCommand對象。另外,如果下層數據庫概要(schema)發生了變動,這些硬編碼將帶來新錯誤。如果使用SqlCommandBuilder對象,應用程序在運行時決定數據庫概要,接受任何改變,相應地建立SQL語句。這不是解決并發性問題,而是完全的避免了該問題,使用戶在不知不覺中覆蓋了他人的更改。

    Try
     daCustomer.Update(dsAllData.Tables(Customer))
    Catch dbcEx As Data.DBConcurrencyException
     Dim dResult As DialogResult
     dResult = MessageBox.Show(messageString, _
     Data Concurrency Exception Occurred, _
     MessageBoxButtons.YesNoCancel, MessageBoxIcon.Error, _
     MessageBoxDefaultButton.Button1, _
     MessageBoxOptions.DefaultDesktopOnly)
     If dResult = DialogResult.Yes Then
      ‘兩個選擇:填充整個表或者刷新該行
      ‘daCustomer.Fill(dsAllData.Tables(Customer))
      UpdateRow(Customer, dbcEx.Row.Item(ID))
     ElseIf dResult = DialogResult.No Then
      ‘保存新行的拷貝
      Dim drCopy As DataRow, drCurrent As DataRow
      drCopy = dsAllData.Tables(Customer).NewRow()
      Dim dc As DataColumn
      drCurrent = dsAllData.Tables(Customer).Rows.Find(dbcEx.Row.Item(ID))
      For Each dc In drCurrent.Table.Columns
       If dc.ReadOnly = False Then
        drCopy.Item(dc.ColumnName) = drCurrent.Item(dc.ColumnName)
      Next

     ‘從數據庫中獲取當前值
     UpdateRow(Customer, dbcEx.Row.Item(ID))

     ‘現在恢復用戶輸入的值并再次保存
     For Each dc In drCurrent.Table.Columns
      If dc.ReadOnly = False Then
       drCurrent.Item(dc.ColumnName) = drCopy.Item(dc.ColumnName)
     Next

     daCustomer.Update(dsAllData.Tables(Customer))
     End If
    End Try

    圖7.捕獲并發性異常

      圖7顯示了一個更好的捕捉該異常的方案。Try...Catch塊捕捉了DBConcurrencyException并給用戶顯示一個標識該錯誤的消息窗口,給用戶提供一個選擇(圖8所示)。這樣我識別已經出現了一個并發性錯誤并有兩個選擇:我可以檢索下層數據并顯示給用戶,強制他們再次作修改,或者我能簡單地使用該用戶指定的改變覆蓋下層數據。這些選項都顯示在消息框中(圖8):


      圖8.處理數據并發性異常

    Private Sub UpdateRow(ByVal TableName As String, ByVal ID As Integer)
     ‘獲取到特定行的引用
     Dim dr As DataRow = dsAllData.Tables(TableName).Rows.Find(ID)

     ‘建立命令更新獲取新的下層數據
     Dim cmd As New SqlClient.SqlCommand(SELECT * FROM & TableName WHERE ID= & ID.ToString(), connCustSvc)

     ‘打開連接并建立數據讀取器(DataReader)
     connCustSvc.Open()
     Dim rdr As SqlClient.SqlDataReader = cmd.ExecuteReader()
     rdr.Read()

     ‘將新數據從數據庫復制到數據行
     Dim dc As DataColumn
     For Each dc In dr.Table.Columns
      If dc.ReadOnly = False Then _
       dr.Item(dc.ColumnName) = rdr.Item(dc.ColumnName)
     Next

     ‘接受數據行中的改變
     dr.AcceptChanges()
     connCustSvc.Close()
    End Sub

    圖9. UpdateRow程序更新緩沖的數據行

      如果用戶決定查看新的下層改變并放棄他們的更改,你只需要簡單地刷新存儲在Customer數據表中的數據。因為DataGrid綁定到數據表,該表格將自動地顯示新數據。為了刷新數據,你有兩種選擇:第一種是使用數據適配器daCustomer的Fill方法來簡單地填充整個數據表。雖然這種技術能夠實現,但是它的花費太大,因為本來你只需要刷新一行。我建立了一個叫UpdateRow的程序,它僅僅讀入有問題的行的數據(圖9)。使用UpdateRow程序時要注意我已經在參數集合中定義了被找到的數據行是單個的、整型關鍵字的列,如果該表有不同數據類型或者復合鍵,你必須重載UpdateRow來處理特定鍵的需求。在數據行和/或數據表用當前數據刷新后,DataGrid在引起并發性異常的行上顯示一個小Error圖標(圖10)。


    圖10.DataGrid顯示Error圖標

      用戶的另一個選擇是忽略對下層數據庫的更改,強迫他的改變生效。有多種方法可以實現該功能。第一種是SQL Server數據庫上直接執行一個SQL語句來使數據表中的數據同步。盡管這種方法可以實現,但是它要求你在數據庫更改時為重寫所有的SQL語句。使用這種技術編寫的SQL是使用的特定數據庫版本的硬編碼(hard-coded),丟失了數據適配器和數據集對象提供的抽象性。

      更好的選擇是使用我前面所寫的UpdateRow程序并且允許數據適配器處理更新。圖9中的代碼首先建立了含有新的更改的數據行的拷貝,接著調用UpdateRow程序從數據庫中獲得新數據。調用UpdateRow過程是必要的,這樣你在試圖再次執行更新時才不會接收到并發性異常。該代碼接著迭代數據行中所有的列,使用用戶提供的值更新最近檢索到的值。最后,使用數據適配器更新下層數據庫。

      這些代碼解決方法都有一些潛在的問題。首先,默認情況下數據適配器的Update方法在第一個并發性異常時就會失敗,不處理后面的數據行。這會導致數據的部分更新或者幾個并發性異常,每一個由用戶單獨處理。圖7中的代碼顯示了另一個潛在的異常,在強迫用戶的更改到下層數據庫的時候。有很小的機會出現另一個用戶在調用UpdateRow程序和執行daCustomer的Update方法之間更改了下層數據庫。這將產生一個未處理的DBConcurrencyException。一種可能的解決方法是把Update方法放入Try...Catch塊,但是第二個Catch代碼可能與第一個相似,并且能潛在的產生它自己的并發性異常,就需要更多的Try...Catch塊,直到無窮。更好的解決方法是將這段代碼放入一個獨立的在多個并發性異常發生的情況下可以調用的程序中。

    使用SQL Server觸發器

      為該公司開發顧客服務應用程序的人員已經處理好Joe更新顧客信息時接收到的DBConcurrencyException異常的代碼,應用程序工作又得很好了--直到下午Sally試圖輸入一個訂單為止。
    Sally接到一個顧客的訂單電話。該顧客信息已經在數據庫中了,因此Sally使用訂單的基本信息包括郵寄地址。接著她打開OrderDetails屏幕并給訂單添加了兩個項目。每個OrderDetail記錄包括OrderID 、ProductID 、Quantity 和Price。訂單填完后,Sally點擊Place Order按鈕把訂單插入數據庫。我通過為OrderDetail記錄添加硬編碼值簡化了代碼(如圖3所示)。

      訂單成功地插入和數據庫,但是Sally接到一個提示,顯示一個項沒有貨了,訂單不能在兩周內發貨。該顧客意識到包裹送達時他不在城里,就詢問是否可以更改發貨地址。Sally在使用Order Maintenance屏幕前已經更改了訂單信息,因此她說樂于幫忙。她打開適當的屏幕并為該顧客改變發貨地址。但是她點擊Save Changes按鈕時,看到了一個與Joe相似的錯誤對話框,應用程序崩潰了。

      理解為什么產生DBConcurrencyException的關鍵在于下層數據庫;叵雸D5中我在OrderDetail表上設置了一個觸發器,所有的INSERTS、UPDATES和DELETES都將調用它。trg_UpdateOrderTotal通過計算Order中每個OrderDetail行的數量乘以價格更新訂單的總價格。當Sally建立一個有兩個項的訂單時,應用程序首先向Orders中插入一個新行,接著向OrderDetail插入兩個新行。在每個OrderDetail記錄建立后,調用觸發器更新Order記錄的Total列。但是這個值只在數據庫中產生并且不會傳遞到應用程序。實際上,圖3中的代碼沒有指定Total,因為在圖1的代碼中已經指定了一個默認值。接著默認值0隨著新的Orders記錄傳遞進SQL Server,該值在訂單建立時是正確的。數據庫接著更新Total列,但是應用程序中的數據表仍然使用0作為訂單總價。當Sally試圖更新Order記錄時,數據適配器認為Total列的值已經改變了并產生一個DBConcurrencyException異常。

      這兒的陷阱是過去當Sally已經更新Order記錄時,碰巧自從最后一次數據自動刷新發生后,她沒有更新剛才建立的新訂單。在建立每個訂單30分鐘內,dsAllData數據集對象被刷新,它將使用正確的合計值更新Orders數據表。任何在數據自動更新(或者應用程序重新啟動)后作的更新將按預計的情形工作。

      這個問題的解決方法與前面的問題相似:刷新用戶看到的數據。但是我能夠更主動,不是等待并發性異常發生。我能在建立OrderDetail記錄后,自動刷新每一個Orders數據行。我能更改圖3中的代碼,使它包含一個UpdateRow程序的調用,在調用daOrderDetail的Update方法后指定Orders中的行。SQL Server將正常完成該觸發器,但是你不能依賴它,因此開發者也許會添加一個時間延遲,這樣觸發器有足夠的時間完成。

      結論

      ADO.NET數據集對象的不連接特性提供了很高的性能,也給應用程序帶來了數據并發性類型的錯誤。了解這些錯誤怎樣和為什么出現將允許你很好地捕捉和處理這種錯誤,給應用程序增加另一個層次的支持。這給用戶維護下層數據的完整性更多的選擇。伴隨著應用程序進一步遷移到互聯網和內部網,用戶成倍增加,并發性問題的處理面臨更大的挑戰。ADO.NET為解決該問題提供了所有的必要工具。

    延伸閱讀

    文章來源于領測軟件測試網 http://www.kjueaiud.com/

    TAG: 處理 數據 異常 dataset 并發


    關于領測軟件測試網 | 領測軟件測試網合作伙伴 | 廣告服務 | 投稿指南 | 聯系我們 | 網站地圖 | 友情鏈接
    版權所有(C) 2003-2010 TestAge(領測軟件測試網)|領測國際科技(北京)有限公司|軟件測試工程師培訓網 All Rights Reserved
    北京市海淀區中關村南大街9號北京理工科技大廈1402室 京ICP備10010545號-5
    技術支持和業務聯系:info@testage.com.cn 電話:010-51297073

    軟件測試 | 領測國際ISTQBISTQB官網TMMiTMMi認證國際軟件測試工程師認證領測軟件測試網

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>