使用 SqlDataReader 執行含輸出參數的預存程序,讀取完資料流後嘗試取得 Parameter.Value 卻拋出例外或值為 null。
param[2].Direction = ParameterDirection.Output;
using (var reader = SQLHelper.ExecuteReader(sql, CommandType.StoredProcedure, param)) {
while (reader.Read()) { list.Add(ConvertReaderToStudentModel(reader)); }
}
// 此處取得 param[2].Value 失敗
pageCount = (int)param[2].Value;
錯誤訊息:「無效嘗試在關閉的資料流上存取參數」**。
SqlDataReader 以串流方式讀取結果集。輸出參數值僅在資料流完全關閉 (Reader 關閉) 後才由伺服器回填至 Parameter 集合。若在 using 區塊結束前 (Reader 尚未 Dispose/Close) 存取輸出參數,會導致錯誤。
// 1. 設定輸出參數方向
param[2].Direction = ParameterDirection.Output;
// 2. 執行讀取
using (var reader = SQLHelper.ExecuteReader(sql, CommandType.StoredProcedure, param)) {
while (reader.Read()) {
list.Add(ConvertReaderToStudentModel(reader));
}
// Reader 仍開啟,此處不可存取輸出參數
}
// 3. Reader 已 Dispose/Close,輸出參數已回填,安全存取
pageCount = (int)param[2].Value;
| 錯誤做法 | 正確做法 |
|---|---|
在 using (reader)... 區塊內存取輸出參數 | 在 using 區塊**外部** (Reader 關閉後) 存取 |
未明確設定 ParameterDirection.Output | 執行前務必設定 param.Direction = ParameterDirection.Output |
使用 ExecuteNonQuery 卻預期回傳結果集 | 需結果集用 ExecuteReader,無結果集用 ExecuteNonQuery |
public PagedResult<T> ExecutePagedQuery<T>(string spName, SqlParameter[] parameters,
Func<SqlDataReader, T> mapFunc, string outputParamName)
{
var outputParam = parameters.First(p => p.ParameterName == outputParamName);
outputParam.Direction = ParameterDirection.Output;
var list = new List<T>();
using (var conn = new SqlConnection(connStr))
using (var cmd = new SqlCommand(spName, conn) { CommandType = CommandType.StoredProcedure })
{
cmd.Parameters.AddRange(parameters);
conn.Open();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read()) list.Add(mapFunc(reader));
}
// Reader 關閉後輸出參數才有值
var totalCount = (int)outputParam.Value;
return new PagedResult<T> { Items = list, TotalCount = totalCount };
}
}
SqlDataReader 串流特性決定輸出參數必須在 Reader 關閉後才能讀取。將存取邏輯移至 using 區塊外部即可解決此經典問題。