企業級應用常需整合既有 Sybase 資料庫,C# 透過 System.Data.Odbc 呼叫預存程序是標準作法。本文詳細說明連線字串設定、參數綁定、執行與資源釋放完整流程,並補充異常處理與最佳實務。
Sybase SYSTEM 11、Adaptive Server Enterprise)System.Data (內建)// 標準 ODBC 連線字串格式
string connStr = "Driver={Sybase SYSTEM 11};" +
"Srvr=MyServerName;" + // ODBC 系統 DSN 或 Server 名稱
"Uid=myUser;" +
"Pwd=myPassword;" +
"Database=myDatabase;"; // 可選,若 DSN 已指定資料庫可省略
注意:驅動程式名稱需與 ODBC 資料來源管理員 中「驅動程式」頁籤顯示的完全一致 (含大小寫、空白)。
using System;
using System.Data;
using System.Data.Odbc;
public class SybaseRepository
{
private readonly string _connectionString;
public SybaseRepository(string connectionString)
{
_connectionString = connectionString;
}
public void ExecuteStoredProc(string procName, params (string Name, OdbcType Type, ParameterDirection Dir, object Value)[] parameters)
{
using (var conn = new OdbcConnection(_connectionString))
using (var cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
// Sybase 預存程序呼叫語法: "owner.proc_name ?, ?, ?"
cmd.CommandText = $"{procName} {string.Join(", ", parameters.Select(_ => "?"))}";
foreach (var (name, type, dir, value) in parameters)
{
var param = cmd.Parameters.Add(name, type);
param.Direction = dir;
param.Value = value ?? DBNull.Value;
}
try
{
conn.Open();
cmd.ExecuteNonQuery();
// 若有 OUTPUT 參數,執行後可從 param.Value 取得
foreach (var (name, _, dir, _) in parameters.Where(p => p.Dir != ParameterDirection.Input))
{
var outParam = cmd.Parameters[name];
// 由呼叫端自行處理 outParam.Value
}
}
catch (OdbcException ex)
{
// 錯誤碼對照: ex.Errors 集合包含多個 OdbcError
throw new InvalidOperationException($"Sybase 執行失敗: {ex.Message}", ex);
}
} // using 區塊自動 Dispose 連線與命令
}
}
var repo = new SybaseRepository(connStr);
// 僅輸入參數
repo.ExecuteStoredProc("dbo.sp_InsertOrder",
("@OrderId", OdbcType.Int, ParameterDirection.Input, 1001),
("@CustName", OdbcType.VarChar, ParameterDirection.Input, "Acme Corp"),
("@Amount", OdbcType.Decimal, ParameterDirection.Input, 1234.56m));
// 包含輸出參數
var outParams = new (string, OdbcType, ParameterDirection, object)[]
{
("@OrderId", OdbcType.Int, ParameterDirection.Input, 1002),
("@NewSeq", OdbcType.Int, ParameterDirection.Output, null),
("@StatusMsg", OdbcType.VarChar, ParameterDirection.Output, null)
};
repo.ExecuteStoredProc("dbo.sp_ProcessOrder", outParams);
// 取得輸出值需在 ExecuteStoredProc 內部暴露或改回傳 Dictionary
// 此處簡化示意,實務建議回傳 Dictionary<string, object>
| 項目 | 說明 |
|---|---|
| CommandText 語法 | Sybase ODBC 需使用 proc_name ?, ?, ? 佔位符號,而非 @param 直接列出 |
| 參數順序 | 嚴格依預存程序定義順序新增 cmd.Parameters.Add,名稱僅供可讀性 |
| 資料型別對應 | OdbcType.VarChar→VARCHAR、OdbcType.Int→INTEGER、OdbcType.Decimal→NUMERIC/DECIMAL、OdbcType.DateTime→DATETIME |
| DBNull.Value | C# null 傳給資料庫必須轉為 DBNull.Value,否則會拋例外 |
| 連線池 | ODBC 連線字串相同會自動啟用連線池,using 區塊結束時實際歸還池而非關閉 |
? 數量與 Parameters.Count 必須一致。ParameterDirection 必須設為 Output 或 InputOutput,且預存程序需 SELECT @param = value 或 SET @param = value。public async Task ExecuteInTransactionAsync(Action action)
{
using (var conn = new OdbcConnection(_connectionString))
{
await conn.OpenAsync();
using (var trans = conn.BeginTransaction())
using (var cmd = conn.CreateCommand())
{
cmd.Transaction = trans;
try
{
action(cmd);
trans.Commit();
}
catch
{
trans.Rollback();
throw;
}
}
}
}
| 方案 | 優點 | 缺點 |
|---|---|---|
| ODBC (本文) | 標準化、跨平台、無額外套件 | 效能較低、功能受限 |
| ASE Client (AdoNet) | 原生效能最佳、支援完整 Sybase 功能 | 需安裝專屬 Provider、僅 Windows |
| Dapper + ODBC | 微型 ORM、程式碼簡潔 | 仍受限於 ODBC 效能 |
| Entity Framework Core | 完整 ORM 體驗 | 需第三方 Provider (如 Sap.Data.Sybase.EF)、配置複雜 |