SQL - Stored Procedure
呼叫 SQL Server 預存程序(Stored Procedure,SP),並把 OUTPUT 參數綁定到 C# model。
Namespace
using ZapLib;
QuickExec
QuickExec<T> 是呼叫 SP 的主要方法。會自動:
- 開連線
- 把 SP 的 OUTPUT 參數綁定到
T的屬性 - 執行 SP
- 把 OUTPUT 值寫回
T物件 - 關連線
Example SP
假設資料庫有這支 SP:
CREATE PROCEDURE dbo.CreateOrder
@userId INT,
@amount MONEY,
@orderId INT OUTPUT,
@status NVARCHAR(20) OUTPUT
AS
BEGIN
INSERT INTO Orders (user_id, amount) VALUES (@userId, @amount);
SET @orderId = SCOPE_IDENTITY();
SET @status = 'created';
END
對應的 C# Model
public class CreateOrderResult
{
public int orderId { get; set; } // SP 的 @orderId OUTPUT
public string status { get; set; } // SP 的 @status OUTPUT
}
規則:
T的每個屬性都會被當成 OUTPUT 參數。屬性名需與 SP 的 OUTPUT 參數名一致(不分大小寫)。
呼叫
SQL db = new SQL("DefaultConn");
CreateOrderResult result = db.QuickExec<CreateOrderResult>(
"dbo.CreateOrder",
new
{
userId = 100,
amount = 1500
}
);
Console.WriteLine($"新訂單 ID: {result.orderId}, 狀態: {result.status}");
INPUT-Only SP
SP 沒有 OUTPUT、只想觸發執行?用 dynamic 當 T,傳空物件即可:
public class EmptyResult { }
db.QuickExec<EmptyResult>(
"dbo.PurgeOldOrders",
new { days = 90 }
);
或乾脆走 QuickQuery<T> — 對 SP 一樣有效(如果 SP 有 SELECT 語句):
public class StatusRow
{
public int total { get; set; }
public int deleted { get; set; }
}
StatusRow[] rows = db.QuickQuery<StatusRow>(
"EXEC dbo.PurgeOldOrders @days = @days",
new { days = 90 }
);
Specify OUTPUT Parameter Size
當 OUTPUT 是 NVARCHAR / VARCHAR 時,預設大小可能太小或太大。用 [SQLType] 精確指定:
using ZapLib;
public class CreateOrderResult
{
public int orderId { get; set; }
[SQLType(SqlDbType.NVarChar, Size = 20)]
public string status { get; set; }
[SQLType(SqlDbType.NVarChar, Size = 200)]
public string message { get; set; }
}
不指定時
NVarChar預設給 4000,通常沒問題但會浪費記憶體。
Multiple Result Sets
QuickExec<T> 只處理 OUTPUT 參數,不處理 SELECT 結果集。如果 SP 同時要回 OUTPUT 又要回多個 result set,得走手動模式:
SQL db = new SQL("DefaultConn");
db.Connect();
if (db.IsConn)
{
try
{
db.Cmd.CommandText = "dbo.ComplexReport";
db.Cmd.CommandType = CommandType.StoredProcedure;
db.Cmd.Parameters.Clear();
db.Cmd.Parameters.AddWithValue("@year", 2026);
var totalParam = new SqlParameter("@total", SqlDbType.Int) { Direction = ParameterDirection.Output };
db.Cmd.Parameters.Add(totalParam);
using (SqlDataReader reader = db.Cmd.ExecuteReader())
{
// 第一個 result set
while (reader.Read())
{
// ...
}
reader.NextResult();
// 第二個 result set
while (reader.Read())
{
// ...
}
}
// OUTPUT 在 reader 關閉後才可讀
int total = (int)totalParam.Value;
}
finally
{
db.Close();
}
}
Error Handling
CreateOrderResult result = db.QuickExec<CreateOrderResult>(
"dbo.CreateOrder",
new { userId = 100, amount = 1500 }
);
// QuickExec<T> 失敗時回傳 default(T) — 通常是 null
if (result == null)
{
Console.WriteLine("SP 執行失敗:" + db.GetErrorMessage());
}
⚠️ 已棄用的 Exec
[Obsolete("Exec<T> 即將棄用,請改用 QuickExec<T> 取代")]
public T Exec<T>(string sql, object param = null)
舊版的 Exec<T> 需要呼叫端自行管理連線,已標記為棄用。新程式碼請一律用 QuickExec<T>。