首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

C# 非同步 SQL Server 存取庫實作:防死鎖、支援預存程序與 T-SQL 的輕量封裝

     編輯:關於SqlServer

設計初衷:為什麼要自己造輪子?

現有資料庫存取庫或過於繁瑣(如 Entity Framework),或靈活度不足(難以支援原生 T-SQL 與非同步模式)。本文實作一個零依賴、純 C#的輕量封裝,核心特性:

  • 支援非同步執行,避免長時間查詢阻塞 UI 執行緒或造成連線池耗盡
  • 統一封裝預存程序與動態 SQL呼叫
  • 內建連線狀態監控,網路斷線自動偵測與事件通知
  • 提供型別安全的資料讀取 Helper,自動處理 DBNull

架構設計:BLL + DAL 分層

BLL(Business Logic Layer)核心基類

維護非同步執行緒池、連線字串建構、命令執行排程、結果回呼處理。不可直接實例化,供 DAL 繼承。

DAL(Data Access Layer)業務實作類

開發者自行實作具體的預存程序與 SQL 呼叫方法。範例:


namespace SQLWrapper {
  public class DAL : BLL {
    public DAL(string server, string db, string user, string pass) {
      base.Start(server, db, user, pass); // 初始化連線字串與背景執行緒
    }
    ~DAL() { base.Stop(eStopType.ForceStopAll); } // 釋放資源
    
    // TODO: 在此新增業務方法
  }
}

同步呼叫模式:預存程序與動態 SQL

預存程序範例


public int MyStoreProcedure(int param1, string param2) {
  // 1. 定義回呼資料容器,指定回傳類型:Scalar/Reader/NonQuery
  var userData = new StoredProcedureCallbackResult(eRequestType.Scalar);
  
  // 2. 設定參數
  userData.Parameters = new SqlParameter[] {
    new SqlParameter("@param1", param1),
    new SqlParameter("@param2", param2)
  };
  
  // 3. 送入執行佇列
  if (!ExecuteStoredProcedure("usp_MyStoreProcedure", userData))
    throw new Exception("Execution failed");
  
  // 4. 同步等待完成(預設 30 秒逾時,可調整 userData.tsWaitForResult)
  if (WaitSqlCompletes(userData) != eWaitForSQLResult.Success)
    throw new Exception("Execution failed");
  
  // 5. 取得結果
  return userData.ScalarValue;
}

動態 SQL 範例


public bool MySQLQuery(int param1, string param2) {
  var userData = new ReaderQueryCallbackResult();
  string sql = $"SELECT TOP(1) * FROM tbl1 WHERE code = {param1} AND name LIKE '%{param2}%'";
  
  if (!ExecuteSQLStatement(sql, userData)) return false;
  if (WaitSqlCompletes(userData) != eWaitForSQLResult.Success) return false;
  
  if (userData.resultDataReader.HasRows && userData.resultDataReader.Read()) {
    // 型別安全讀取,自動處理 DBNull
    int field1 = GetIntValueOfDBField(userData.resultDataReader["Field1"], -1);
    string field2 = GetStringValueOfDBField(userData.resultDataReader["Field2"], null);
    DateTime? field3 = GetDateValueOfDBField(userData.resultDataReader["Field3"], null);
  }
  userData.resultDataReader.Dispose();
  return true;
}

非同步呼叫模式:不阻塞呼叫執行緒


// DAL 端:啟動非同步操作並立即回傳控制權
public StoredProcedureCallbackResult MyStoreProcedureASYNC(int param1, string param2) {
  var userData = new StoredProcedureCallbackResult(eRequestType.Reader);
  userData.Parameters = new SqlParameter[] { ... };
  ExecuteStoredProcedure("usp_MyStoreProcedure", userData);
  return userData; // 立即回傳,未等待完成
}

// 呼叫端:輪詢檢查狀態
var userData = myDal.MyStoreProcedureASYNC(10, "hello");
while (true) {
  switch (myDal.WaitSqlCompletes(userData, 10)) { // 每次等 10ms
    case eWaitForSQLResult.Waiting: continue;
    case eWaitForSQLResult.Success: 
      // 處理 userData.resultDataReader
      break;
    default: throw new Exception("Execution failed");
  }
}

關鍵細節:逾時控制與連線狀態

  • 連線逾時:連線字串中 Connection Timeout,預設 15 秒
  • 命令執行逾時:userData.tsWaitForResult,預設 30 秒,可調整:userData.tsWaitForResult = TimeSpan.FromSeconds(15);
  • 資料庫狀態事件:OnDatabaseStatusChanged 事件在連線斷開/恢復時觸發,支援自動重連邏輯

支援的回呼資料類型

類型適用場景結果存取屬性
ScalarQueryCallbackResult單一值查詢ScalarValue
ReaderQueryCallbackResult結果集查詢resultDataReader
NonQueryCallbackResultINSERT/UPDATE/DELETEAffectedRows
StoredProcedureCallbackResult預存程序(需指定 eRequestType)依類型而定

總結

這個輕量封裝解決了三大痛點:非同步防阻塞、型別安全讀取、靈活支援預存程序與動態 SQL。完整程式碼可直接整合至專案,無額外 NuGet 依賴,適合追求效能與控制力的專案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved