在 .NET 應用中調用 Oracle 存儲過程或執行 DML 時,使用 OracleCommand 搭配 OracleParameter 實現參數化查詢,是防止 SQL 注入、提升執行計劃復用率的核心最佳實踐。本文通過完整代碼示例,演示參數綁定、連接管理、異常處理的規範寫法。
public class SatiGrade
{
public string SatiTypeName { get; set; }
public int SatiTypeValue { get; set; }
}
public bool InsertSatiGrade(SatiGrade grade)
{
if (grade == null) return false;
const string sql = "INSERT INTO sati_type VALUES (sati_type_seq.NEXTVAL, :gName, :gValue)";
try
{
using (var cmd = new OracleCommand(sql, GetConnection()))
{
// 綁定參數:名稱、類型、長度、值
cmd.Parameters.Add(new OracleParameter("gName", OracleDbType.Varchar2, 80)
{
Value = grade.SatiTypeName
});
cmd.Parameters.Add(new OracleParameter("gValue", OracleDbType.Int32)
{
Value = grade.SatiTypeValue
});
cmd.Connection.Open();
cmd.ExecuteNonQuery();
return true;
}
}
catch (Exception ex)
{
// 實際項目中應接入日志框架(如 Serilog、NLog)
Console.WriteLine($"InsertSatiGrade 失敗: {ex.Message}");
return false;
}
}
private OracleConnection GetConnection()
{
// 連接字符串建議從配置文件讀取
const string connStr = "Data Source=ORCL;User Id=app_user;Password=****;Pooling=true;Min Pool Size=5;Max Pool Size=100;";
return new OracleConnection(connStr);
}
| 維度 | 建議 | 說明 |
|---|---|---|
| 參數命名 | 使用冒號前綴 :param_name |
Oracle 要求參數標記以冒號開頭,且不區分大小寫 |
| 類型顯式聲明 | 指定 OracleDbType 與長度 |
避免隱式轉換導致計劃失效或精度丟失 |
| 連接生命周期 | using 塊自動釋放 |
確保連接及時歸還池,防止洩漏 |
| 批量操作 | 使用 OracleCommand.ArrayBindCount |
大幅降低網絡往返,提升吞吐 |
| 異常處理 | 捕獲具體異常 OracleException |
可根據 Number 區分唯一約束、死鎖等錯誤碼 |
var cmd = new OracleCommand("pkg_hr.proc_get_salary", conn)
{
CommandType = CommandType.StoredProcedure
};
cmd.Parameters.Add("p_emp_id", OracleDbType.Int32, 1001, ParameterDirection.Input);
var pOut = cmd.Parameters.Add("p_salary", OracleDbType.Decimal, ParameterDirection.Output);
cmd.ExecuteNonQuery();
decimal salary = ((OracleDecimal)pOut.Value).Value;
參數化查詢不僅是安全底線,更是性能優化的起點。養成「顯式類型、using 管理連接、異常細粒度捕獲」的編碼習慣,能讓 .NET + Oracle 應用更穩健、可維護。