
簡介面向 .NET 平臺連接 Oracle 數(shù)據(jù)庫的 64 位組件合集覆蓋 ODP.NET 4 與 2.0、ASP.NET Providers、OLE DB、Microsoft Transaction Server 服務及 Instant Client版本為 12.2.0.1.0。適合需要在 ASP.NET、WinForm 或服務端程序中接入 Oracle 的開發(fā)人員與運維人員用于免安裝或 xcopy 方式快速部署數(shù)據(jù)訪問組件減少環(huán)境配置與版本匹配上的反復排查。壓縮包共 179 個文件約 73.48MB以 67 個 dll 為主另含 38 個 sql、17 個 plb、14 個 sym、10 個 config、10 個 exe 和 9 個 bat 等分別對應程序集、數(shù)據(jù)庫腳本、符號與配置、安裝卸載及注冊批處理等用途。已有 1545 人學習說明其在 Oracle 客戶端與 .NET 互操作場景中具有一定參考價值。目錄中的批處理與配置項可輔助完成注冊、卸載和環(huán)境校驗配合 SQL 與示例配置便于讀者搭建可復用的連接環(huán)境并理解 ODP.NET 與 OLE DB 等組件的部署差異。1. 免安裝的 Oracle 數(shù)據(jù)訪問層ODAC 12.2 xcopy 包能解決什么服務器只給了你一個目錄的寫權限裝不了 Oracle 客戶端可 ASP.NET 站點第二天就要連庫——ODAC122010Xcopy-x64.zip 就是為這種場面準備的。它把 ODAC 12.2 的 ODP.NET、OLE DB、ORAMTS 以純文件形式交付解壓落位、補齊路徑和配置進程起來就能用全程不碰安裝器也不用去改 machine.config。適合 .NET Framework 4.x 的 Web 與桌面程序適合要把數(shù)據(jù)庫驅動固化進部署包的流水線也適合機器上已經(jīng)躺著兩三個版本 Oracle 客戶端、再加一個就要打架的老環(huán)境。反過來指望圖形化配 TNS、指望 ORAMTS 分布式事務開箱即用、或者團隊里沒人愿意讀 PATH 和注冊表的裝完整客戶端更省心。2. 包里有什么、該選哪個ODP.NET4、OLE DB 與 ORAMTS 的邊界拿到壓縮包先別急著解壓到站點目錄里。這個包里同時塞了四五套彼此獨立的數(shù)據(jù)訪問組件它們共享一部分原生庫但注冊方式、調用方式、依賴條件完全不同。選錯了組件后面所有排查都是在錯誤的地基上打補丁。先花十分鐘把目錄結構過一遍比上線后盯著BadImageFormatException猜要劃算得多。2.1 四個組件不是一回事先認清你要的是哪一個按目錄粗分xcopy 包通常包含這么幾塊odp.net下的托管與非托管 ODP.NET、oledb下的 Oracle Provider for OLE DB 與 ORAMTS 相關文件、asp.net下的 ASP.NET 提供程序、以及 ODBC 那一套。它們的定位差別很大組件主要文件調用方式是否依賴原生庫典型場景ODP.NET 非托管驅動Oracle.DataAccess.dllOracle.DataAccess.Client命名空間是依賴 OCI 系列原生庫老 .NET Framework 項目需要非托管驅動的高級特性ODP.NET 托管驅動Oracle.ManagedDataAccess.dllOracle.ManagedDataAccess.Client否純托管新項目、部署環(huán)境干凈、不想管 PATHOLE DB 提供程序OraOLEDB.dllOleDbConnection、SSIS、報表工具是且需要 COM 注冊只能走 OLE DB 的第三方工具鏈ORAMTSoramts 相關文件System.Transactions/ COM 隱式登記是且依賴恢復服務Oracle 與其它資源管理器混在一個分布式事務里選型的第一條判斷標準很簡單代碼里using的是哪個命名空間。寫著Oracle.DataAccess.Client的只有非托管驅動能接寫著Oracle.ManagedDataAccess.Client的托管驅動就夠了而且完全不用碰 PATH——這是最省事的一條路。只有當項目里出現(xiàn)OleDbConnection加ProviderOraOLEDB.Oracle這種連接串才必須動 OLE DB 那一套。第二條判斷標準是事務模型。如果你的代碼里出現(xiàn)TransactionScope并且同一個事務里既寫了 Oracle 又寫了 SQL Server 或 MQ那就是分布式事務非托管驅動會嘗試通過 ORAMTS 登記成資源管理器。這一塊是 xcopy 部署最容易翻車的地方后面單獨說。2.2 xcopy 與安裝版的差別省掉的是什么代價是什么安裝版 ODAC 干了四件事往 GAC 里放程序集、往 machine.config 里寫DbProviderFactories節(jié)點、往注冊表里寫 OLE DB 的 COM 注冊信息、注冊 Windows 服務并把原生庫路徑塞進 PATH。xcopy 版一件都不干它只給你文件。省下來的是管理員權限和“裝完影響全機器”的心理負擔代價是這四件事得你自己在應用層面補齊。好處也很實在應用隔離。同一臺機器上兩個站點一個要 11.2 的非托管驅動一個要 12.2 的安裝版只能裝一個xcopy 版可以各帶各的目錄、各寫各的 web.config互不干擾。GAC 里沒有注冊也就意味著不會出現(xiàn)“升級客戶端把別人的老應用搞掛”這種事故。代價集中在三處。第一GAC 里沒有程序集綁定完全靠bin目錄和配置里的版本號寫錯一個數(shù)字就是FileLoadException。第二machine.config 里沒有DbProviderFactories.GetFactory(Oracle.DataAccess.Client)直接拋“未找到指定提供程序”必須在應用配置文件里補。第三ORAMTS 依賴的恢復服務是 Windows 服務xcopy 包里的文件只是組件服務本體需要單獨注冊拿不到就別指望分布式事務。版本上還有一條硬邊界這個 12.2 的包面向 .NET Framework 4.x程序集版本形如4.122.x.x。項目如果已經(jīng)遷到 .NET Core 或更高版本的 .NET包里這套托管驅動幫不上忙得換對應的新版本組件。開工前先確認web.config里的targetFramework別解壓完了才發(fā)現(xiàn)用不上。3. 部署實操把壓縮包解到站點目錄并讓進程找得到部署這件事的難點不在“解壓”在“讓 w3wp.exe 在正確的時刻、從正確的目錄、加載正確的那份 DLL”。Windows 的 DLL 搜索順序、IIS 的應用池回收時機、GAC 的優(yōu)先級三者疊加起來足夠讓人懷疑人生。下面這套順序是我自己反復用過的照著走能把大部分不確定性提前消掉。3.1 解壓與目錄布局路徑、位數(shù)、命名三件事路徑要短、要純英文、要固定。三個要求都不是潔癖路徑里有空格會讓某些命令行參數(shù)解析出問題路徑里有中文在部分編碼環(huán)境下會讀到亂碼文件名路徑不固定則意味著每次換機器都要改配置配置文件一進版本庫就變成了環(huán)境耦合。# 解壓到短路徑、純英文目錄不要放 C:\Program Files 或帶中文的路徑 Expand-Archive -Path .\ODAC122010Xcopy-x64.zip -DestinationPath D:\odac\x64 -Force # 先看清目錄結構再決定哪個文件夾進 PATH Get-ChildItem D:\odac\x64 -Directory -Recurse -Depth 2 | Select-Object -ExpandProperty FullName-DestinationPath一旦定下來就別再改后面配置里的路徑、PATH 里的條目、運維文檔里的說明全都指向它。-Force用于覆蓋上一次部署的殘留文件避免新舊 DLL 混在一個目錄里——這種混裝排查起來極其難受因為文件名一樣、大小不同。解壓完立刻做位數(shù)確認。這個包是 x64 的原生組件只有 64 位版本如果站點跑在“啟用 32 位應用程序”的應用池里加載原生庫時必然失敗。確認方式不是看操作系統(tǒng)而是看應用池設置# 列出所有應用池的 32 位開關True 的那些站點不能直接用 x64 原生組件 Import-Module WebAdministration Get-IISAppPool | Select-Object Name, {nEnable32Bit;e{ (Get-Item IIS:\AppPools\$($_.Name)).enable32BitAppOnWin64 }}這一步的產(chǎn)出應該是一張清單哪些應用池是 32 位的哪些是 64 位的。32 位的要么改成 64 位前提是它依賴的其它組件也支持要么這個包對它無效。別想著兩邊都兼容混合位數(shù)部署最后一定會亂。3.2 讓進程找得到原生庫PATH、權限與進程重啟托管程序集好辦拷到站點bin目錄就行CLR 會去那兒找。原生庫不好辦非托管 ODP.NET 和 OLE DB 在運行時要LoadLibrary一批ora*.dll、oci.dll這些文件的搜索路徑跟著宿主進程走而宿主是w3wp.exe不在你的站點目錄里。把原生 DLL 丟進bin賭它被找到成功與否取決于宿主怎么發(fā)起加載不建議賭。常見做法是改機器級 PATH把組件目錄加進去# 追加而不是覆蓋覆蓋 PATH 是能把整臺機器搞癱的操作 $old [Environment]::GetEnvironmentVariable(PATH,Machine) $new D:\odac\x64\odp.net\bin\4;D:\odac\x64\oledb\bin;$old [Environment]::SetEnvironmentVariable(PATH, $new, Machine) # 給應用池身份讀執(zhí)行權限缺權限的表現(xiàn)是“找不到模塊”而不是“拒絕訪問” icacls D:\odac\x64 /grant IIS_IUSRS:(OI)(CI)(RX) /T改完 PATH 必須重啟 IIS因為w3wp.exe只在啟動時繼承環(huán)境變量光回收應用池不夠# 回收應用池不會重新讀取機器環(huán)境變量必須重啟 W3SVC iisreset /restart(OI)(CI)(RX)是繼承到子目錄的讀加執(zhí)行權限夠用且最小化。注意 PATH 的追加順序把 ODAC 目錄放在$old前面是為了讓它優(yōu)先于機器上可能已存在的其它 Oracle 客戶端目錄被搜索到。這個順序在后面排查“加載了錯誤版本 DLL”時是關鍵線索別隨手改成追加到末尾。3.3 托管程序集落位與版本綁定托管 DLL 拷貝到站點bin這是唯一推薦的位置。不要往 GAC 里塞——gacutil本身就未必有而且塞進去之后應用之間的隔離性就沒了等于把 xcopy 的最大優(yōu)勢扔掉。# 托管程序集進站點 bin原生庫走 PATH兩條路徑分開管理 $bin D:\sites\demo\bin Copy-Item D:\odac\x64\odp.net\bin\4\Oracle.DataAccess.dll $bin -Force # 讀真實版本號和公鑰標記配置里填的 version 必須和這里完全一致 $an [Reflection.AssemblyName]::GetAssemblyName($bin\Oracle.DataAccess.dll) [PSCustomObject]{ Name $an.Name Version $an.Version.ToString() Token ($an.GetPublicKeyToken() | ForEach-Object { $_.ToString(x2) }) -join }這段輸出的Version和Token是下一步寫配置的輸入不要憑記憶填。網(wǎng)上流傳的版本號五花八門抄錯了就是Could not load file or assembly ... Versionx.x.x.x。同一個程序集名在不同小版本之間版本號會變公鑰標記通常穩(wěn)定但版本號必須實測。如果機器 GAC 里已經(jīng)存在同名程序集比如以前裝過完整客戶端CLR 可能優(yōu)先加載 GAC 里那份舊版本導致bin里的文件形同虛設。判斷方法在最后一章的驗證環(huán)節(jié)里給。真要強制走本地文件就在配置文件里加bindingRedirect把舊版本重定向到你實際帶的版本。4. 配置實操DbProviderFactories 注冊與連接串參數(shù)文件到位只是讓程序“能加載”配置到位才讓它“能用”。這一章的三塊配置各管一件事DbProviderFactories管工廠方法能不能拿到提供程序connectionStrings管連接怎么建立和復用OLE DB 與 ORAMTS 的額外注冊管特定的調用路徑。三塊都補齊才算真正跑通。4.1 DbProviderFactories讓 GetFactory 認得 ODP.NET安裝版會往 machine.config 寫節(jié)點xcopy 版不寫所以應用配置文件里必須先remove再add。remove不能省——機器上如果存在安裝版留下的舊條目不 remove 會直接報重復鍵。configuration system.data DbProviderFactories !-- 先移除防止機器上安裝版留下的同名條目沖突 -- remove invariantOracle.DataAccess.Client / add nameODP.NET, Unmanaged Driver invariantOracle.DataAccess.Client descriptionOracle Data Provider for .NET, Unmanaged Driver typeOracle.DataAccess.Client.OracleClientFactory, Oracle.DataAccess, Version4.122.1.0, Cultureneutral, PublicKeyToken89b483f429c47342 / /DbProviderFactories /system.data /configuration四個屬性各有分工。invariant是代碼里傳的名字必須和GetFactory(...)的字符串一模一樣大小寫不敏感但拼寫必須對。type是三段式程序集限定名完整類型名、程序集名、版本加公鑰標記缺一段都會加載失敗。name和description只影響可讀性但填清楚對后來接手的人友好。版本號那一段用 3.3 節(jié)實測出來的值替換。如果項目代碼直接new OracleConnection()而不用工廠這段配置可以不加但只要代碼里出現(xiàn)DbProviderFactories.GetFactory或者在用依賴工廠抽象的 ORM、報表組件就必須加。4.2 連接串描述符、TNS_ADMIN 與 EZConnect連接串的寫法決定了要不要依賴tnsnames.ora。xcopy 部署最怕的就是“機器上沒配 TNS”所以我的默認選擇是直接把完整描述符寫進連接串徹底繞開解析環(huán)節(jié)connectionStrings add nameora connectionStringUser Idapp_user;Password***;Data Source(DESCRIPTION(ADDRESS(PROTOCOLTCP)(HOST10.0.0.10)(PORT1521))(CONNECT_DATA(SERVICE_NAMEORCLPDB1)));Poolingtrue;Min Pool Size2;Max Pool Size80;Connection Timeout15;Statement Cache Size50;Self Tuningfalse; providerNameOracle.DataAccess.Client / /connectionStrings參數(shù)逐個說明。Data Source用描述符而不是別名省掉TNS_ADMIN和tnsnames.ora的全部麻煩代價是連接信息進了配置文件需要走配置加密或環(huán)境變量注入。Poolingtrue是默認值顯式寫出來是為了讓人看見。Min Pool Size2讓應用啟動后立刻有可用連接避免首個請求承擔建連延遲Max Pool Size80要低于數(shù)據(jù)庫端的會話上限除以實例數(shù)拍腦袋填 200 是連接池打滿的常見起點。Connection Timeout15短于大多數(shù)網(wǎng)關的超時能讓故障快速暴露而不是把線程掛死。Statement Cache Size50緩存游標對反復執(zhí)行同一批 SQL 的站點收益明顯。Self Tuningfalse只在連接數(shù)很多的場景下建議關掉默認開著會周期性做負載探測。代碼側用工廠拿連接換驅動時只改配置不改代碼// 走工廠而不是 new換托管/非托管驅動時只動 web.config var factory DbProviderFactories.GetFactory(Oracle.DataAccess.Client); using (var conn factory.CreateConnection()) { conn.ConnectionString ConfigurationManager.ConnectionStrings[ora].ConnectionString; conn.Open(); using (var cmd conn.CreateCommand()) { // 用 dual 做最小連通性驗證比 select 1 更貼近 Oracle 的實際語法 cmd.CommandText select sys_context(userenv,sid) from dual; var sid cmd.ExecuteScalar(); } }CreateConnection返回的是DbConnection基類后面所有操作都通過基類接口完成這樣遷移到托管驅動時業(yè)務代碼零改動。這段代碼同時也是最好的冒煙測試能拿到sid說明工廠注冊、程序集加載、連接串解析、網(wǎng)絡連通四件事全部通過。4.3 OLE DB 與 ORAMTS 的啟用條件OLE DB 是 COM 組件光有文件不夠必須注冊到系統(tǒng)里# COM 組件注冊/s 靜默失敗時去掉 /s 看錯誤彈窗 regsvr32 /s D:\odac\x64\oledb\bin\OraOLEDB.dll注冊成功只解決了一半。OLE DB 提供程序還需要一個 ProgID 到 CLSID 的映射鍵安裝版會自動寫xcopy 版通常要手工補。別從網(wǎng)上抄 GUID——regsvr32執(zhí)行后到注冊表里查這個 DLL 實際注冊出來的 CLSID用真實的那個建OraOLEDB.Oracle鍵。抄錯 GUID 的表現(xiàn)是“提供程序已注冊但仍然找不到”比沒注冊更難查。ORAMTS 的條件更嚴格。它需要在系統(tǒng)里有一個恢復服務來協(xié)調兩階段提交xcopy 包里的文件只是客戶端側組件。沒有這個服務TransactionScope提升為分布式事務時會在登記階段失敗。判斷自己是否真的需要它如果整個事務只碰一個 Oracle 庫那就是本地事務根本走不到 ORAMTS只有跨庫、跨資源管理器才需要。多數(shù)所謂“需要分布式事務”的場景拆開成補償邏輯反而更可靠。5. 避坑與排查五類上線后才會暴露的問題下面這五條都是本地跑得好好的、一上服務器就出問題的類型。共同點是錯誤信息指向的方向和真正的原因往往差得很遠照著報錯原文去搜十有八九搜到的是另一個場景的答案。5.1 加載期的三類翻車現(xiàn)象應用一啟動就拋“未能加載文件或程序集”或“試圖加載格式不正確的程序”。原因位數(shù)不匹配或者程序集版本號和配置里寫的不一致。前者發(fā)生在應用池開了 32 位模式卻加載 x64 原生庫后者發(fā)生在配置文件里的Version和bin里實際文件的版本對不上。解決先用 3.2 節(jié)的命令確認應用池位數(shù)再用 3.3 節(jié)的腳本讀出真實版本號覆蓋配置。兩條都確認過還報錯就去查 GAC 里是不是有同名舊版本搶先加載了?,F(xiàn)象代碼里明明用的是bin下的新 DLL運行行為卻像舊版本某些新方法報MissingMethodException。原因GAC 里存在同名程序集CLR 的加載順序讓它優(yōu)先命中 GAC。xcopy 部署最常見的隱形對手就是這個。解決在配置里加bindingRedirect強制重定向到實際版本或者干脆在目標機器上卸掉舊的安裝版客戶端。判斷當前到底加載了哪份文件用最后一章的命令看進程模塊列表?,F(xiàn)象OLE DB 連接報“未在本地計算機上注冊‘OraOLEDB.Oracle’提供程序”。原因regsvr32沒執(zhí)行、執(zhí)行失敗往往是權限不夠或者注冊了但缺少 ProgID 映射鍵。解決以管理員身份重跑regsvr32成功后查注冊表確認 CLSID 真實存在再補 ProgID 鍵。64 位進程只認 64 位注冊信息32 位工具能連通不代表站點能連通。5.2 連接與事務期的兩類翻車現(xiàn)象報 ORA-12154“無法解析指定的連接標識符”或者 ORA-01804 初始化 OCI 失敗。原因前者是連接串用了別名但沒有可解析的tnsnames.ora或者TNS_ADMIN指向了錯誤目錄后者是進程加載到了別的路徑下的oci.dll版本和驅動不配套。解決優(yōu)先改成 4.2 節(jié)的完整描述符寫法繞開解析。如果必須用別名把TNS_ADMIN顯式設成組件目錄并把tnsnames.ora放進去同時確認 PATH 里 ODAC 目錄排在其它客戶端目錄之前?,F(xiàn)象單庫操作正常一進TransactionScope就報事務登記失敗或超時。原因事務被提升為分布式事務但 ORAMTS 的恢復服務不存在或沒啟動。解決先確認是否真的跨了資源管理器只碰一個庫的話改用OracleTransaction顯式事務問題直接消失。確實需要跨庫就得把恢復服務補齊這時候 xcopy 的便利性讓位給安裝版更省事。6. 驗證與調優(yōu)確認加載的是哪一份 DLL配置全對、程序能跑仍然不能算部署完成——你得能證明進程里掛的是你以為的那份文件。這件事在出故障時價值最大所有“版本不對”的懷疑都能用一條命令終結。# 看站點進程實際加載了哪些 Oracle 相關模塊路徑比版本號更能說明問題 Get-Process w3wp -ErrorAction SilentlyContinue | ForEach-Object { $proc $_ $_.Modules | Where-Object { $_.ModuleName -match ^(Oracle|ora) } | Select-Object {nPid;e{$proc.Id}}, ModuleName, FileName } | Format-Table -AutoSizeFileName那一列就是你想要的全部答案。它如果指向D:\odac\x64\...說明 PATH 生效了如果指向某個老的客戶端 home說明搜索順序被別人搶了回頭調 PATH 順序。ModuleName里出現(xiàn)oramts說明進程確實在嘗試分布式事務路徑這和代碼里有沒有TransactionScope能互相印證。一個常見誤解是把“能連上庫”當成部署成功的標志。連接成功只覆蓋了托管層和網(wǎng)絡層原生庫的版本兼容問題往往要等到某條特定 SQL、某個 LOB 操作才暴露。所以我的習慣是連接驗證之外再加一步模塊路徑核對兩件事都過才算收工。參數(shù)上還有兩個值值得按真實負載調Statement Cache Size在 SQL 重復率高的站點可以提到 100 以上但每個緩存游標都占服務端資源盲目調大反而增加共享池壓力Max Pool Size直接決定數(shù)據(jù)庫端會話數(shù)的峰值先在測試環(huán)境壓一遍看實際并發(fā)再乘個 1.2 的系數(shù)比直接抄網(wǎng)上的數(shù)字靠譜得多。從那以后我每上一個新環(huán)境第一件事都是先跑一遍模塊路徑核對再談功能驗證——省下來的不是排查時間是不用在下班前半小時懷疑人生的運氣。希望幫到你。本文還有配套的精品資源點擊獲取