封裝 Oracle 資料庫常用操作 (存儲過程、SQL 語句、DataReader、DataSet),提供統一的連線管理、參數處理與資源釋放機制,作為資料存取層 (DAL) 的基礎類別,減少重複程式碼並強化可維護性。
using System;
using System.Configuration;
using System.Data;
using Oracle.DataAccess.Client;
namespace WIS.Base.Data
{
///
/// Oracle 資料存取基類,封裝連線、命令建構、執行邏輯
///
public class DbObject : IDisposable
{
#region 成員變數
protected OracleConnection Connection { get; private set; }
private readonly string _connectionString;
private bool _disposed = false;
#endregion
#region 建構函式
/// 使用設定檔預設連線字串
public DbObject()
{
_connectionString = ConfigurationManager.AppSettings["ConnectionString"];
Connection = new OracleConnection(_connectionString);
}
/// 指定自訂連線字串
public DbObject(string connectionString)
{
_connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString));
Connection = new OracleConnection(_connectionString);
}
#endregion
#region 私有輔助方法
/// 建構 OracleCommand (存儲過程)
private OracleCommand BuildCommand(string storedProcName, IDataParameter[] parameters)
{
var cmd = new OracleCommand(storedProcName, Connection)
{
CommandType = CommandType.StoredProcedure
};
if (parameters != null)
foreach (var p in parameters)
cmd.Parameters.Add(p);
return cmd;
}
#endregion
#region 執行存儲過程
/// 執行存儲過程,回傳影響列數與 ReturnValue
public object RunProcedure(string storedProcName, IDataParameter[] parameters, out int rowsAffected)
{
EnsureConnectionOpen();
var cmd = BuildCommand(storedProcName, parameters);
rowsAffected = cmd.ExecuteNonQuery();
// 尋找 ReturnValue 參數
var returnParam = Array.Find(parameters, p => p.Direction == ParameterDirection.ReturnValue);
return returnParam?.Value;
}
/// 執行存儲過程,回傳 OracleDataReader (呼叫端負責關閉)
public OracleDataReader RunProcedure(string storedProcName, IDataParameter[] parameters)
{
EnsureConnectionOpen();
var cmd = BuildCommand(storedProcName, parameters);
// CommandBehavior.CloseConnection: 讀取器關閉時自動關閉連線
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
/// 執行存儲過程,填充 DataSet (指定表名)
public DataSet RunProcedure(string storedProcName, IDataParameter[] parameters, string tableName)
{
var ds = new DataSet();
FillDataSet(storedProcName, parameters, ds, tableName);
return ds;
}
/// 執行存儲過程,填充既有 DataSet
public void RunProcedure(string storedProcName, IDataParameter[] parameters, DataSet dataSet, string tableName)
{
FillDataSet(storedProcName, parameters, dataSet, tableName);
}
private void FillDataSet(string storedProcName, IDataParameter[] parameters, DataSet ds, string tableName)
{
EnsureConnectionOpen();
using (var adapter = new OracleDataAdapter(BuildCommand(storedProcName, parameters)))
{
adapter.Fill(ds, tableName);
}
// 注意:使用 DataAdapter 會自動開啟/關閉連線,這裡不額外 Close
}
#endregion
#region 執行 SQL 語句
public int ExecNonQuery(string sql)
{
EnsureConnectionOpen();
using (var cmd = new OracleCommand(sql, Connection))
{
return cmd.ExecuteNonQuery();
}
}
public OracleDataReader ExecSqlString(string sql)
{
EnsureConnectionOpen();
var cmd = new OracleCommand(sql, Connection);
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
public DataSet ExecSqlString(string sql, string tableName)
{
var ds = new DataSet();
FillDataSetFromSql(sql, ds, tableName);
return ds;
}
public void ExecSqlString(string sql, DataSet dataSet, string tableName)
{
FillDataSetFromSql(sql, dataSet, tableName);
}
private void FillDataSetFromSql(string sql, DataSet ds, string tableName)
{
EnsureConnectionOpen();
using (var adapter = new OracleDataAdapter(sql, Connection))
{
adapter.Fill(ds, tableName);
}
}
public object ExecScalar(string sql)
{
EnsureConnectionOpen();
using (var cmd = new OracleCommand(sql, Connection))
{
return cmd.ExecuteScalar();
}
}
#endregion
#region 連線管理與資源釋放
private void EnsureConnectionOpen()
{
if (Connection.State != ConnectionState.Open)
Connection.Open();
}
public void Close()
{
if (Connection.State == ConnectionState.Open)
Connection.Close();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
Connection?.Close();
Connection?.Dispose();
}
_disposed = true;
}
}
~DbObject()
{
Dispose(false);
}
#endregion
}
}
EnsureConnectionOpen() 統一檢查並開啟連線,避免重複 Open() 擲出例外CommandBehavior.CloseConnection,讀取完畢自動關閉連線IDataParameter[] 介面,支援 OracleParameter 等具體實作ReturnValue 方向參數,簡化存儲過程回傳值取得IDisposable,支援 using 區塊確定性釋放_disposed flag) 防止重複釋放~DbObject) 作為安全網,但不建議依賴 GC| 方法簽章 | 適用場景 |
|---|---|
| RunProcedure(..., out rowsAffected) | INSERT/UPDATE/DELETE,需知道影響列數 |
| RunProcedure(...) : OracleDataReader | 大量資料流式讀取,記憶體效率佳 |
| RunProcedure(..., tableName) : DataSet | 中小資料量,需離線操作/綁定 UI |
| RunProcedure(..., dataSet, tableName) | 合併多查詢結果至單一 DataSet |
// 1. 執行存儲過程新增資料
using (var db = new DbObject())
{
var params = new OracleParameter[]
{
new OracleParameter("p_id", OracleDbType.Int32, 101, ParameterDirection.Input),
new OracleParameter("p_name", OracleDbType.Varchar2, "測試商品", ParameterDirection.Input),
new OracleParameter("p_result", OracleDbType.Int32, ParameterDirection.ReturnValue)
};
int rows;
var result = db.RunProcedure("PKG_PRODUCT.ADD_PRODUCT", params, out rows);
Console.WriteLine($"影響列數: {rows}, 回傳代碼: {result}");
}
// 2. 查詢資料綁定 GridView
using (var db = new DbObject())
{
DataSet ds = db.ExecSqlString("SELECT * FROM PRODUCTS WHERE STATUS = 1", "Products");
gridView.DataSource = ds.Tables["Products"];
gridView.DataBind();
}
// 3. 大量資料流式處理
using (var db = new DbObject())
using (var reader = db.ExecSqlString("SELECT * FROM BIG_TABLE"))
{
while (reader.Read())
{
// 處理每一列,不將所有資料載入記憶體
ProcessRow(reader);
}
}
ExecuteReaderAsync、FillAsync 等方法,配合 async/await 釋放執行緒Pooling=true;Min Pool Size=5;Max Pool Size=100Param.Input("p_id", 101)這個 DbObject 基類展示了 ADO.NET 經典時代的資料存取封裝精髓:連線管理、命令建構、多樣化執行方式與資源釋放。雖然現代開發多轉向 ORM,但理解其運作機制對於效能調優、舊系統維護、以及處理複雜存儲過程邏輯仍具備不可替代的價值。