# CommandRepository 使用注意事項 ## 1. 交易(Transaction)處理 若需要在同一個交易中執行多個資料庫操作,務必傳入相同的 IDbTransaction 實例,否則每個方法都會建立自己的連線與交易。 可以使用 ExecuteInTransactionAsync 封裝多個操作,確保成功才提交(Commit)、失敗則回滾(Rollback)。 注意:不同連線的交易彼此獨立,無法共用。不要跨連線傳遞 IDbTransaction。 ## 2. 連線生命週期 如果未傳入 tran,每個方法會自行建立並關閉連線(短連線模式)。 若要重複查詢或批次作業,建議使用同一個交易與連線,減少連線建立成本。 勿在交易外長時間持有連線,避免造成連線池耗盡。 ## 3. SQL 與參數 sql 參數請確保使用參數化查詢(@paramName),避免 SQL Injection。 param 物件的屬性名稱必須與 SQL 參數名稱一致。 若 param 為 null,請確保 SQL 語法中沒有必填的參數。 ## 4. 泛型方法(T 型別限制) InsertAsync<T>、UpdateAsync<T>、DeleteAsync<T> 等方法依賴 JsonProperty/Column 屬性 做欄位對應,確保模型類別有正確標註。 T 必須為 參考型別(class),不支援值型別(struct)。 ## 5. 大量資料處理 大量插入請使用 BatchInsertAsync,效能優於逐筆呼叫 InsertAsync。 批次查詢請避免一次回傳過多資料,可考慮分頁(TOP / OFFSET FETCH)。 ## 6. 多結果集查詢 QueryMultipleAsync 需配合 Dapper.SqlMapper.GridReader 使用,讀取結果時順序必須與 SQL 的結果集一致。 務必在使用完後釋放 GridReader,避免連線被佔用。 ## 7. 錯誤處理 ExecuteInTransactionAsync 會在 action 中發生例外時自動 Rollback。 非交易方法需自行處理例外,必要時記錄 SQL 與參數以便除錯。 SQL 連線失敗或逾時會拋出 SqlException,請在呼叫端適當處理重試或回復機制。 ## 8. 擴充方法依賴 目前的 CRUD 方法依賴 InsertByJsonPropertyAsync、UpdateByJsonPropertyAsync 等 自訂擴充方法,請確保 cmdb_portal.Extensions 中的實作已正確載入。 若模型與資料表欄位不同,請確保擴充方法的對應邏輯正確,否則可能導致欄位缺失或更新失敗。 # ICommandRepository ``` using my.Extensions; using Dapper; using Microsoft.Data.SqlClient; using System.Data; namespace my.Repository { /// <summary> /// 資料庫命令操作介面,提供泛型的新增、修改、刪除、查詢與交易控制方法。 /// </summary> public interface ICommandRepository { /// <summary> /// 新增單筆資料。 /// </summary> /// <typeparam name="T">資料模型型別。</typeparam> /// <param name="entity">要新增的資料物件。</param> /// <param name="tran">可選,既有的資料庫交易。</param> Task<long> InsertAsync<T>(T entity, IDbTransaction tran = null) where T : class; /// <summary> /// 更新單筆資料。 /// </summary> /// <typeparam name="T">資料模型型別。</typeparam> /// <param name="entity">要更新的資料物件。</param> /// <param name="tran">可選,既有的資料庫交易。</param> Task<bool> UpdateAsync<T>(T entity, IDbTransaction tran = null) where T : class; /// <summary> /// 依 ID 刪除單筆資料。 /// </summary> /// <typeparam name="T">資料模型型別。</typeparam> /// <param name="id">資料的主鍵 ID。</param> /// <param name="tran">可選,既有的資料庫交易。</param> Task<bool> DeleteAsync<T>(long id, IDbTransaction tran = null) where T : class; /// <summary> /// 批次新增資料。 /// </summary> /// <typeparam name="T">資料模型型別。</typeparam> /// <param name="entities">要新增的資料集合。</param> /// <param name="tran">可選,既有的資料庫交易。</param> Task<long> BatchInsertAsync<T>(IEnumerable<T> entities, IDbTransaction tran = null) where T : class; /// <summary> /// 執行任意 SQL 語法(INSERT/UPDATE/DELETE)。 /// </summary> /// <param name="sql">SQL 語法字串。</param> /// <param name="param">SQL 參數物件。</param> /// <param name="tran">可選,既有的資料庫交易。</param> Task<int> ExecuteSqlAsync(string sql, object param, IDbTransaction tran = null); /// <summary> /// 執行 SQL 並回傳單一純量值。 /// </summary> /// <typeparam name="T">回傳值型別。</typeparam> /// <param name="sql">SQL 語法字串。</param> /// <param name="param">SQL 參數物件。</param> /// <param name="tran">可選,既有的資料庫交易。</param> Task<T> ExecuteScalarAsync<T>(string sql, object param, IDbTransaction tran = null); /// <summary> /// 執行多結果集查詢。 /// </summary> /// <param name="sql">SQL 語法字串。</param> /// <param name="param">SQL 參數物件,可為 null。</param> /// <param name="tran">可選,既有的資料庫交易。</param> Task<SqlMapper.GridReader> QueryMultipleAsync(string sql, object param = null, IDbTransaction tran = null); /// <summary> /// 查詢多筆資料。 /// </summary> /// <typeparam name="T">回傳資料模型型別。</typeparam> /// <param name="sql">SQL 語法字串。</param> /// <param name="param">SQL 參數物件,可為 null。</param> /// <param name="tran">可選,既有的資料庫交易。</param> Task<IEnumerable<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction tran = null); /// <summary> /// 查詢第一筆資料(若無資料則回傳預設值)。 /// </summary> /// <typeparam name="T">回傳資料模型型別。</typeparam> /// <param name="sql">SQL 語法字串。</param> /// <param name="param">SQL 參數物件,可為 null。</param> /// <param name="tran">可選,既有的資料庫交易。</param> Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction tran = null); /// <summary> /// 開啟交易並在交易中執行指定方法。 /// </summary> /// <param name="action">要在交易中執行的非同步方法,接受 <see cref="IDbTransaction"/> 參數。</param> Task ExecuteInTransactionAsync(Func<IDbTransaction, Task> action); } } ```