該方法 `ExecuteScalar(string sql, SqlParameter[] parameters)` 封裝了 **執行單值查詢/存儲過程** 的通用邏輯:打開連接、創建命令、處理參數、執行、關閉連接、返回首行首列值。
public static object ExecuteScalar(string sql, SqlParameter[] parameters) {
// 1. 參數預處理:null 值轉 DBNull.Value (數據庫要求)
if (parameters != null) {
foreach (SqlParameter p in parameters) {
if (p.Value == null)
p.Value = DBNull.Value; // 防止傳 null 導致異常
}
}
// 2. 連接對象 (using 確保釋放)
using (SqlConnection con = new SqlConnection(ConnectionString)) {
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
// 3. 判斷命令類型:有參數 → 存儲過程,無參數 → 文本 SQL
// ⚠️ 注意:此邏輯有缺陷,見下文分析
if (parameters != null) {
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddRange(parameters);
} else {
cmd.CommandType = CommandType.Text;
}
cmd.CommandText = sql;
try {
con.Open();
// 4. 執行並返回 object (可能為 null/DBNull/具體值)
return cmd.ExecuteScalar();
} catch {
// 5. 異常吞沒,返回空字符串 (掩蓋真實錯誤,不推薦)
return string.Empty;
}
} // using 結束,自動 con.Close()/Dispose()
}
**有參數 ≠ 存儲過程**。參數化 SQL 文本 (`CommandType.Text`) 同樣需要參數。
// 改進:顯式傳入命令類型
public static object ExecuteScalar(string sql, CommandType cmdType, SqlParameter[] parameters) {
// ...
cmd.CommandType = cmdType; // 調用者決定:Text / StoredProcedure
// ...
}
// 調用示例
ExecuteScalar("SELECT COUNT(*) FROM Users WHERE Age > @age", CommandType.Text,
new SqlParameter("@age", 18));
ExecuteScalar("usp_GetUserCount", CommandType.StoredProcedure,
new SqlParameter("@city", "Beijing"));
// 改進:拋出異常或返回 Result 模式
public static object ExecuteScalar(string sql, CommandType cmdType, SqlParameter[] parameters) {
using (var con = new SqlConnection(ConnectionString))
using (var cmd = new SqlCommand(sql, con) { CommandType = cmdType }) {
if (parameters != null) cmd.Parameters.AddRange(parameters);
con.Open();
return cmd.ExecuteScalar(); // 讓異常拋出,上層統一處理/記錄
}
}
建議從配置文件讀取,或注入 `IDbConnection` 工廠,便於測試與多庫支持。
public static class SqlHelper {
private static readonly string ConnStr = ConfigurationManager.ConnectionStrings["Default"].ConnectionString;
private static readonly ILog Log = LogManager.GetLogger(typeof(SqlHelper));
public static object ExecuteScalar(string sql, CommandType cmdType, params SqlParameter[] parameters) {
using (var con = new SqlConnection(ConnStr))
using (var cmd = new SqlCommand(sql, con) { CommandType = cmdType }) {
if (parameters != null) cmd.Parameters.AddRange(parameters);
con.Open();
Log.Debug($"Exec Scalar: {sql} | Params: {string.Join(",", parameters?.Select(p=>$"{p.ParameterName}={p.Value}") ?? new string[0])}");
return cmd.ExecuteScalar();
}
}
// 異步版本 (高併發必備)
public static async Task
// 統計用戶數
int count = Convert.ToInt32(SqlHelper.ExecuteScalar(
"SELECT COUNT(*) FROM Users WHERE Status = @status",
CommandType.Text,
new SqlParameter("@status", 1)
));
// 調用存儲過程獲取新 ID
object newId = SqlHelper.ExecuteScalar(
"usp_InsertOrder",
CommandType.StoredProcedure,
new SqlParameter("@UserId", 100),
new SqlParameter("@Amount", 99.9m)
);
原代碼實現了基本流程,但存在 **命令類型判斷錯誤、異常吞沒、硬編碼連接串** 等生產級缺陷。掌握 **「顯式命令類型、參數化防注入、using 資源管理、異常拋出不吞沒、異步支持、日志記錄」** 才是合格的 ADO.NET 封裝水平。