Microsoft Enterprise Library Data Access Application Block (EntLib) 簡化 ADO.NET 資料存取。呼叫 Oracle 預存程序回傳結果集時,EntLib 有特定約定:單一結果集自動識別 cur_out 參數;多重結果集需手動註冊 OracleParameter。本文記錄 EntLib 5.0 + Oracle 10g/11g 實測可行的完整模式。
CREATE OR REPLACE PROCEDURE p_sel_top_count (
in_top IN NUMBER,
out_count OUT NUMBER,
cur_out OUT SYS_REFCURSOR
) AS
BEGIN
SELECT COUNT(*) INTO out_count FROM userinfo;
OPEN cur_out FOR SELECT * FROM userinfo WHERE id < in_top;
END;
/
關鍵約定: OUT 游標參數必須命名為 cur_out,EntLib 內部硬編碼識別此名稱,改其他名稱將無法自動回傳結果集。
using Microsoft.Practices.EnterpriseLibrary.Data;
using Microsoft.Practices.EnterpriseLibrary.Common;
using System.Data;
using System.Data.Common;
Database db = DatabaseFactory.CreateDatabase("OracleConnectionString");
DbCommand cmd = db.GetStoredProcCommand("p_sel_top_count");
// 輸入參數
db.AddInParameter(cmd, "in_top", DbType.Int32, 10);
// 輸出參數 (標量)
db.AddOutParameter(cmd, "out_count", DbType.Int32, 4);
// 注意:cur_out 不需手動 AddOutParameter,EntLib 會自動處理
DataSet ds = db.ExecuteDataSet(cmd);
// ds.Tables[0] 即為 cur_out 對應的結果集
int totalCount = Convert.ToInt32(db.GetParameterValue(cmd, "out_count"));
dataGridView1.DataSource = ds.Tables[0];
CREATE OR REPLACE PROCEDURE p_sel_multi_result (
in_top IN NUMBER,
out_count OUT NUMBER,
cur_out_1 OUT SYS_REFCURSOR,
cur_out_2 OUT SYS_REFCURSOR
) AS
BEGIN
SELECT COUNT(*) INTO out_count FROM userinfo;
OPEN cur_out_1 FOR SELECT * FROM userinfo WHERE id < in_top;
OPEN cur_out_2 FOR SELECT * FROM userinfo WHERE id < 5;
END;
/
Database db = DatabaseFactory.CreateDatabase("OracleConnectionString");
DbCommand cmd = db.GetStoredProcCommand("p_sel_multi_result");
db.AddInParameter(cmd, "in_top", DbType.Int32, 10);
db.AddOutParameter(cmd, "out_count", DbType.Int32, 4);
// 【關鍵】必須顯式建立 OracleParameter 並加入命令
// 需引用 Oracle.DataAccess.Client (ODP.NET)
using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;
OracleParameter pCursor1 = new OracleParameter("cur_out_1", OracleDbType.RefCursor);
pCursor1.Direction = ParameterDirection.Output;
cmd.Parameters.Add(pCursor1);
OracleParameter pCursor2 = new OracleParameter("cur_out_2", OracleDbType.RefCursor);
pCursor2.Direction = ParameterDirection.Output;
cmd.Parameters.Add(pCursor2);
// 執行
DataSet ds = db.ExecuteDataSet(cmd);
// ds.Tables[0] -> cur_out_1, ds.Tables[1] -> cur_out_2
// 順序與預存程序 OPEN 游標順序一致
int total = Convert.ToInt32(db.GetParameterValue(cmd, "out_count"));
<connectionStrings>
<add name="OracleConnectionString"
connectionString="Data Source=ORCL;User Id=scott;Password=tiger;"
providerName="Oracle.DataAccess.Client" />
</connectionStrings>
<dataConfiguration defaultDatabase="OracleConnectionString" />
providerName 必須為 Oracle.DataAccess.Client (ODP.NET),而非 System.Data.OracleClient (已淘汰)。
| 現象 | 原因 | 解決 |
|---|---|---|
| 單一結果集 ds.Tables.Count = 0 | OUT 游標參數名非 cur_out | 將預存程序參數重命名為 cur_out,或改用多重結果集模式手動註冊 |
ORA-06550: PLS-00306 | 參數型別/順序不匹配 | 檢查 C# 端 AddInParameter/AddOutParameter 順序與 PL/SQL 宣告一致 |
| 多重結果集只有第一個有資料 | 未正確加入所有 OracleParameter | 確認每個 OUT 游標都 new OracleParameter(..., OracleDbType.RefCursor) 並 cmd.Parameters.Add() |
| 找不到 OracleDbType | 專案未引用 ODP.NET | NuGet 安裝 Oracle.ManagedDataAccess 或 Oracle.DataAccess |
cur_out,多重結果集命名 cur_out_1, cur_out_2... 並對應手動註冊。ExecuteProcSingleResult、ExecuteProcMultiResult,減少重複代碼。測試環境:EntLib 5.0.505 + Oracle 11g R2 + ODP.NET 4.121.2 (Managed)。EntLib 6 版本行為一致。若遷移至 .NET Core,建議改用 Dapper + Oracle.ManagedDataAccess 直接呼叫,更輕量。