天天看點

使用AO往MDB和SDE寫資料的一些經驗之談

使用AO往MDB和SDE寫資料的一些經驗之談

使用AO往MDB和SDE寫資料的一些經驗之談往Personal GDB或SDEGDB中寫入要素記錄是一件非常簡單的事情,但似乎太多的情況下,寫入資料的用例過于簡單,使得許多代碼問題、特别是ArcSDE本身的問題和Error無法暴露,很多人也從未意識到類似的問題。我最近正在做類似的工作,代碼編寫似乎不難,但測試就要了命了,發現的問題數不勝數,頭痛不已。

1.插入記錄的效率問題

向要素類中插入記錄有兩種方式,一是IFeature.Store,另一個是IFeatureCursor.Insert(IFeatureBuffer)和IFeatureCursor.Flush方法,顯而易見的,後一種方法由于使用了緩存,速度上比前者快。

2.往MDB和SDE要素類中插入記錄

這兩種類型的要素類在往其中插入資料時不一定需要使用IWorkspaceEdit接口來開啟和關閉一個Session,但是,如果SDE的要素類被注冊為version,這個接口就必須使用了,否則CPU會高達100%,并會爆出“the operation in invalid on aclosed state”的錯誤。而使用了有版本的SDE要素類,我遇到了許多令人崩潰的SDEERROR,比如FDO_E_SE_DB_IO_ERROR、FDO_E_SE_OUT_OF_LOCKS等等。

3.将多個MDB導入一個MDB的時候,如果使用了IWorkspaceEdit接口,會出現某幾個圖層無法用ArcMap或ArcCatalog打開的情況,即以打開程式就崩潰,而不是用該接口,其它相同的代碼做的加載過程,一切正常。

4.将兩個或以上MDB導入一個MDB時候,到第二個MDB,很可能發生“**_SHAPE_INDEX被占用”的情況,這是因為寫.NET平台代碼時,你周遊或插入資料的遊标cursor未釋放的緣故。pFeatureCursor=null;并不會将對象從記憶體中清除,這是因為.NET平台是由Runtime來收集垃圾的,不像VC或VB那樣能直接銷毀COM對象,此時你應該使用System.Runtime.InteropServices.Marshal.ReleaseComObject方法來強制釋放COM對象,以解除對某個表的獨占狀态。

關于釋放SDE連接配接的問題

如果我們嘗試往SDE中使用AO代碼加載大量的資料,比如每個圖層50萬條記錄,一共30個圖層,那麼這個過程是個不折不扣的噩夢,你會遇到許多匪夷所思的問題,一般而言,出了問題總是會報個fdoerr号,我們也可以查一查,但如果出現的問題号屬于SDE ERROR類型,就不好說了,你會發現某些情況整個網絡上都沒有看到過,就是一個孤例。比如我遇到過的FDO_E_SE_DB_IO_ERROR和FDO_E_SE_OUT_OF_LOCKS等。

我在往SDE中寫代碼導入資料時候頻繁遇到FDO_E_SE_DB_IO_ERROR問題,盡管safe網站上給出了該問題的三種可能原因,但我一直都沒能從Oracle的角度解決。統計一下往SDE導入資料成功的情況,每次導入一個要素類,即每個要素類導入時都給開啟一個專用的gsrvr程序,成功率是最高的,幾乎為95%。 不成功的10%是一次導入200萬條标注要素類,但分開島,每次50萬條,也一切安好。

那麼問題就來了,我們知道,SDE安裝後,SDE與DBMS之間有一個giomgr程序負責管理雙方的通訊過程,即根據通路請求建立gsrvr程序。一般地,gsrvr将處理與SDE有關的查詢,存儲,删除等操作,并且它能處理多個連接配接請求,它與DBMS的關系就是“通則不痛,通則不痛”,一旦傳輸的資料量多了,gsrvr就開始抽風了。我們的問題是,如何讓一個要素類處理完成後,釋放這個連接配接,即關閉該gsrvr程序,在下一個要素類開始時再次建立一個新gsrvr程序。這個方式的确也是最為保險的。

現在問題就是,如何釋放SDE連接配接,事實上,如果使用純COM對象來寫,這幾乎不是問題,有人認為将工作空間對象設定為null即可。比如我們寫個VB的代碼:

dim pFWS as IFeatureWorkspace

set pFWS=SdeWorkspaceFactory.open()  '1

set pFWS=nothing                                      '2

去看看Oracle的連接配接,在1執行完後,應該有兩個SDE連結,而在執行完2以後,就隻有一個SDE連結了,其所有者是giomgr。顯然,SDE連接配接被關閉了。

那麼在.NET平台中,這種setpFWS=nothing是行不通的,我們得使用System.Runtime.InteropServices.Marshal.ReleaseComObject(pFWS);才能讓非托管代碼在記憶體中被清除。試一試,也可以發現SDE的連接配接關閉。

将問題再複雜一點,如果我們周遊一次pFWS中的某個要素類,會發現即使使用System.Runtime.InteropServices.Marshal.ReleaseComObject(pFWS);也搞不定,解決的方法是,将你在代碼中new出來的所有的AO對象都用System.Runtime.InteropServices.Marshal.ReleaseComObject方法釋放,某些循環産生的對象,如:

pFeature=pFeatureCursor.NextFeature();

while(pFeature!=null)

{

    System.Runtime.InteropServices.Marshal.ReleaseComObject(pFeature);

    pFeature=pFeatureCursor.NextFeature();

}

System.Runtime.InteropServices.Marshal.ReleaseComObject(pFeatureCursor);

也必須銷毀,如果有一個與SDE資料庫相關的對象未銷毀,則仍然存在兩個SDE連接配接。

使用這種一個圖層一個gsrvr程序的方法後,再未出現過FDO_E_SE_DB_IO_ERROR問題。

關于Network I/O Error的錯誤

好了,繼續我最近關于操作SDE海量資料庫的心得。我開發的一個功能是将MDB中的要素寫入SDE要素類中,這個功能并不複雜,我早就寫過一個非常完備的LoadFeatureClass函數,但在加載要素之前,我還需要在SDE庫中進行一些操作,也就是對每周遊一個MDB中的要素類時,必須對SDE庫中對應的兩個要素類進行一些處理,結果,就非常“幸運”地頻繁遇到了Network I/O Error[xxx(某個要素類)]。

根據異常的提示,似乎SDE與Oracle之間發生了“不愉快”的事情,即連結斷了,但這個可能性馬上被我否定了,要知道我在初步測試用的是本機,不可能發生這種情況。那第二個原因就是gsrvr.exe這個程序爆掉了,導緻出現了這種異常假象,果然不出我的預測,在每次出現這個異常後,Oracle的會話中隻有一個SDE會話(如果連接配接上SDE,标準應該有兩個SDE會話,一個是SDE監聽會話,另一個是連結會話)。

gsrvr.exe這個連結程序為什麼會爆掉?我在網上看了一些相關的文檔,列舉了七七八八的解決之道:

  • 将Oracle9更新到9.2.0.3.0之後的版本【我更新到了9.2.0.7.0,沒解決】 
  • 将Oracle9更新到Oracle10g【問題依舊】 
  • 給SDE91打上SP2更新檔,sde91-genpatch3-ora9i-win更新檔等【無效】 
  • 将Oracle的SPA/GPA調大【無效】 
  •                         dbinit.sde添加:“set DISABLE_SPATIAL_CACHE=TRUE”【依然無效】

會不會是我的代碼問題呢?經過反複檢查,這個因素被排除了,因為有幾個相對資料量較小的MDB導入一切順利,沒有發生任何問題。

删除MDB中幾個要素類看看?果然,在删除了4個要素類後,頻繁出現的Network I/O Error問題居然消失了。

綜合前面發生的許多異常和這個問題,我最後得出了一個推論:一個SDE程序gsrvr.exe一次隻能在SDE庫中産生有限個數的要素遊标,并且在每個遊标上能夠傳輸的資料是有限的,否則gsrvr.exe就會爆掉。這兩個因素是同時存在的,因為一個gsrvr.exe周遊SDE多個要素類不會産生問題,隻産生一個要素遊标卻傳輸上百萬條資料也不會産生問題。

有鑒于此,最後的解決方法仍然是周遊了14-18個要素類後就強制關閉SDE連結,然後再産生SDE連結繼續操作。雖然犧牲了時間,但卻取得了可靠性。

繼續閱讀