# 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);
}
}
```