熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

ADO.NET Entity Framework 試水——並發

2013-11-13 09:40:11  來源: .NET編程 

  提到數據庫操作特別是企業級的數據庫應用就不得不提一個多人操作時經常會產生的問題——並發沖突本文首先來看一下什麼是並發沖突傳統的並發沖突有現有的處理方式最後結合EF看一個處理並發沖突的實例
  
  一要完成本文中的實例您需要作如下准備
  
  將Visual Studio NET Framework 升級到SP點擊轉到升級地址
  安裝SQL SERVER VS 中自帶的EXPRESS版的SQL SERVER應該也可以用
  下載並附加數據庫點擊下載DemoDbV
  創建一個VB Console Application並且取一個合適的名字(例如Concurrency之類的)注意目標Framework要設置成
  
  
  二什麼是並發沖突
  
  讓我們來看一個跟取款相關的例子某年某月某日某時某分老王在A取款機取錢他兒子小王同時在B取款機取錢(不要問我為什麼這麼巧^_^)他倆從同一個賬號上取於是就發生了如下一序列的操作
  
  A取款機向中央數據庫提問這賬上還有多少錢?
  
  B取款機向中央數據庫詢問這賬上還有多少錢?
  
  中央數據庫回答A取款機W
  
  中央數據庫回答B取款機W
  
  然後老王對A取款機說我要取出W
  
  同時小王對B取款機說我要取出W
  
  A取款機就算了一下WW=W>於是就吐出W現金給了老王並且准備告訴中央數據庫現在還剩W啦但是就在它告訴中央數據庫之前發生了以下的事情
  
  B取款機計算了一下W(此時它還不知道余額已經成W了因為A取款機還沒有告訴中央數據庫)減去W等於W大於於是就吐出W現金給了小王然後它當然也要知會中央數據庫
  
  中央數據庫於是收到A取款機的消息這個賬號還剩W於是刷新余額為W然後又收到B取款機說還剩W於是就刷新余額為W
  
  呵呵於是小王+老王的賬戶裡一共存有W元結果老王取了W元小王取了W元賬戶裡卻還剩了W元!~@#$%^&
  
  這就是一種並發沖突由於同一時間有兩個或者多個端在對同一數據進行操作從而導致數據發生了錯誤如果取款機真的以這樣的方式來處理並發那麼我現在就不寫這片文章了——趕緊發動全家對表說好了在某一時刻同時取錢去^_^


  三常見的並發沖突處理方式
  
  一般來說我們把並發沖突處理方式歸結為
  
  第一類放任不管方式第二類開放式並發處理方式第三類保守式並發處理方式
  
  
  
   放任不管方式
  
  與其說這是一種處理並發沖突的方式不如說它是一種沒有對並發沖突做任何處理的方式但是在許多過去的系統裡由於沒有考慮到多用戶網絡應用等情況這種處理方式還真存在於不少系統中
  
  舉例來說AB兩人從數據庫中獲取了同一個筆記本的信息例如IBM ThinkPad T然後A把牌子改成了Lenovo ThinkPadB把型號改成了T A然後他們開始提交了此時如果A先提交然後B提交那麼最後的結果是IBM ThinkPad T A反之則變成Lenovo ThinkPad T
  
  總之一句話誰最後提交誰老大想像一下如果A修改了個屬性的值B修改了個屬性的值那麼對於先提交的A來說這將是一個多麼慘痛的打擊:)
  
  雖然這種放任不管的方式似乎不太負責任但是其處理性能卻是相對較高的
  
  
  
   開放式並發處理
  
  開放式並發處理老外叫做Optimistic Concurrency——樂觀的並發這種並發處理方式要求我們對並發抱有一種樂觀的態度百分之九十九點九九不會發生並發沖突萬一發生了系統也能捕獲到沖突或者根據策略自動處理或者就提醒一下用戶讓用戶來決定是不是要繼續提交
  
  仍然用上面的例子來說這事兒AB兩個人同時獲取了筆記本的信息IBM ThinkPad T然後……(此處跟上例做一樣的修改直到提交)此時如果A先提交那麼B提交的時候系統會發現哎喲不好有並發沖突了就會拋個異常給B讓B知道發生並發沖突了然後B就可以根據實際情況選擇相應的處理策略(比如繼續提交進行覆蓋或者取消提交等等)相反如果B先提交那麼A提交時就會得到相應的提醒
  
  這樣的並發處理方式可以說在可靠性與性能上取得平衡適合於對數據可靠性要求不是特別嚴格需要較高的性能並且不會大量發生並發的場合
  
  
  
   保守式並發處理
  
  這是最為嚴謹的一種並發沖突的處理方式它把並發轉化為了串行操作
  
  例如A從數據庫中獲取了筆記本信息IBM ThinkPad TB也要對其進行修改但此時由於A已經從數據庫中將數據取出因此B被置於等待狀態直到A把數據修改完提交了數據庫數據更新為Lenovo ThinkPad T此時數據庫才把數據給B那麼B就可以在Lenovo ThinkPad T的基礎上把它修改為Lenovo ThinkPad T A而在B提交前其它一切針對此記錄的操作都得排除等著B
  
  這樣子當然非常理想由於不存在並發自然也就消除了並發沖突的問題但是這種鎖也存在著較為隱蔽的風險如果A修改了數據一直不提交或者A因為故障沒有辦法提交那麼其它所有的相關的操作都將被阻礙住因此只有對數據准確性要求極高並且用戶可以忍受等待的情況下使用這種並發沖突的處理方法


   
  四EF並發沖突處理實例
  
  EF發布時提供了兩種並發沖突處理方式放任不管方式和開放式並發默認采用放任不管的方式處理
  
  如果要使用開放式並發那麼必須設置相應屬性上的Concurrency Mode值為Fixed我們先對實體類的屬性進行修改讓其支持開放式並發然後來模擬一個並發的序列看看怎麼來處理並發沖突
  
  當前數據庫情況如圖所示
  
  
  
  
  第一步在概念模型設計器裡按照圖所示分別把NotebookBrand和NotebookType屬性的Concurrency Mode設置為Fixed
  
  然後我們寫一段代碼來模擬一個並發沖突的情況
  
  Create object context
  
  Dim objContext As New NbWhEntities()
  
  Dim objContext As New NbWhEntities()
  
  
  
  Query the same record as entities
  
  Dim laptop = (From aLaptop In objContextNotebook _
  
   Where aLaptopId = _
  
   Select aLaptop)FirstOrDefault()
  
  
  
  Dim laptop = (From aLaptop In objContextNotebook _
  
   Where aLaptopId = _
  
   Select aLaptop)FirstOrDefault()
  
  
  
  Modify the entity
  
  laptopBrand = Lenovo ThinkPad
  
  laptopType = laptopType & A
  
  
  
  Submit st object context
  
  objContextSaveChanges()
  
  
  
  Try
  
   Submit nd object context and cause cuncurrency exception
  
   objContextSaveChanges()
  
  Catch ex As OptimisticConcurrencyException
  
   Using refresh method to
  
   objContextRefresh(ObjectsRefreshModeStoreWins laptop)
  
   We should load the new data from db and ask user to change it again
  
   objContextSaveChanges()
  
  End Try
  
  
  我們創建了兩個Object Context分別查詢出了同一個實體第一個修改其品牌為Lenovo ThinkPad第二個同時將其型號修改為T A然後第一個實體保存然後第二個保存由於我們在Brand屬性上設置了Concurrency Mode為Fixed而此時laptop中的Brand屬性的值應該是一開始取得的T而數據庫裡的值是Lenovo T於是系統就會拋出OptimisticConcurrencyException(開放式並發異常)當程序捕獲到異常以後就可以使用Object Context的Refresh方法對異常采取處理由於沒有在刷新laptop以後未對其作任何修改故最終結果將與laptop提交時的結果一致
  
  這裡Refresh的第一個參數值得注意一下它是一個枚舉值有兩個選項StoreWins或者是ClientWins見名知義如果是StoreWins那麼Refresh以後laptop的值將與數據庫裡的對應記錄的值一致(修改會丟失)而如果ClientWins則laptop的值保持並且提交以後會把objContext提交的修改覆蓋
  
  其實這兩種方法均不完美總會導致一部分修改丟失但是這總比在不知情的情況下的覆蓋要好
  
  另外需要說明上面的方法只是對並發沖突的一種模擬這樣的模式在處理並發沖突時會有問題一般的處理方法是當檢測到並發沖突時提示用戶會重新從數據庫載入數據然後讓用戶在新數據的情況下重新修改後再次提交直到不再有並發沖突發生
  
  這樣看似可能成為一個無窮盡的痛苦的過程但實際上由於這種處理方式是基於對並發沖突的樂觀估計來設計的因此當我們認為並發沖突很少有可能發生時這種處理方式可以有效避免數據被無意識的覆蓋問題
  
  
  
  五示例代碼下載
  
  點擊下載
  


From:http://tw.wingwit.com/Article/program/net/201311/11293.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.