iT邦幫忙

0

DbAccess 大幅改版:簡化資料存取與交易管理

  • 分享至 

  • xImage
  •  

dbaccess-v2

BeeNET 於 v3.4.0 在資料存取層進行了大幅重構,過去的 DbCommandHelperSysDb 等輔助類別,現在都統整為單一的 DbAccess 類別。
開發者不再需要手動管理 DbConnectionDbCommandDbDataReader,即可輕鬆完成資料操作。此外,DbAccess 也支援批次命令與交易,以及外部連線的整合,讓使用更安全、彈性。本文整理了改版重點與實際使用方式。


1️⃣ 改版重點總覽

  • 單一入口 DbAccess:所有資料存取皆透過 DbAccess,降低耦合度並減少學習成本。
  • 自動連線管理:內部模式下自動建立/釋放 DbConnectionDbCommandDbDataReader
  • 同步/非同步 API:所有主要操作皆提供 Sync/Async 版本。
  • 交易與批次
    • ExecuteBatch* 可一鍵包交易;任一命令失敗即回滾,並拋出包含失敗索引的例外。
    • 單筆命令也可透過 Execute(spec, DbTransaction) / ExecuteAsync(spec, DbTransaction) 參與外部交易。
  • 外部連線模式:可與自訂交易範圍整合。

2️⃣ DbAccess API 概觀

1) 建構子(兩種模式)

// 內部連線模式:以 databaseId 解析 Provider 與 ConnectionString
var db1 = new DbAccess("HR");

// 外部連線模式:連線與交易生命週期由外部管理
using (var conn = new SqlConnection(connString))
{
    conn.Open();
    var db2 = new DbAccess(conn); // 外部連線型態與 Provider 對應由框架管理
}

2) 單筆命令

// 同步
DbCommandResult result = db1.Execute(spec);

// 非同步
DbCommandResult resultAsync = await db1.ExecuteAsync(spec, cancellationToken);

3) 批次命令與交易

var batch = new DbBatchSpec
{
    UseTransaction = true,
    IsolationLevel = IsolationLevel.ReadCommitted, // 可選
    Commands =
    {
        new DbCommandSpec(DbCommandKind.NonQuery, "INSERT INTO Department(Name) VALUES({0})", "R&D"),
        new DbCommandSpec(DbCommandKind.NonQuery, "INSERT INTO Employee(Name, DepartmentId) VALUES({0}, {1})", "Alice", 5)
    }
};

// 同步
DbBatchResult batchResult = db1.ExecuteBatch(batch);

// 非同步
DbBatchResult batchResultAsync = await db1.ExecuteBatchAsync(batch, cancellationToken);

若批次第 i 筆失敗:框架會嘗試回滾並拋出
InvalidOperationException($"Failed to execute batch: Command at index {i} failed.", innerException),便於快速定位。

4) 指定外部交易的單筆命令

using (var conn = new SqlConnection(connString))
{
    conn.Open();
    using (var tran = conn.BeginTransaction())
    {
        var db = new DbAccess(conn);
        db.Execute(spec1, tran);                   // 同步
        await db.ExecuteAsync(spec2, tran, token); // 非同步
        tran.Commit();
    }
}

⚠️ 注意:若使用外部交易 (DbTransaction),請僅呼叫 Execute / ExecuteAsync,不應再呼叫 ExecuteBatch

5) 串流查詢與物件映射

// Query:直接回傳 List<T>
var list = db1.Query<Employee>(spec);

// QueryAsync:直接回傳 List<T>
var listAsync = await db1.QueryAsync<Employee>(spec, cancellationToken);

6) DataTable 回寫

var spec = new DataTableUpdateSpec
{
    DataTable = table,
    InsertCommand = insertSpec,
    UpdateCommand = updateSpec,
    DeleteCommand = deleteSpec,
    UseTransaction = true,
    IsolationLevel = IsolationLevel.ReadCommitted // 可選
};

var db = new DbAccess("HR");
int affected = db.UpdateDataTable(spec);

3️⃣ DbCommandSpec 說明

DbCommandSpec 是資料庫命令的中介類別:你只需描述「要做什麼」(SQL + 參數 + 命令種類),它就會在執行前依照 DatabaseType 自動轉換成正確的 DbCommand 與 DbParameter。

  • 兩種參數模式

    • 位置參數{0}, {1}, …(短小、直覺)
    • 具名參數{name}, {hiredOn}, …(欄位多時更不易放錯)
  • 依 DatabaseType 套用參數前綴
    你在 SQL 中寫 {name}{0},實際執行時會自動轉換為正確的參數名稱與 DbParameter

    • SQL Server:@name
    • Oracle::name
    • MySQL:?name
  • Stored Procedure 直接傳遞
    CommandType = StoredProcedure 時,CommandText 不做參數占位符解析,直接用 SP 名稱與參數集合建立命令。

  • Timeout 規則
    CommandTimeout <= 0 以預設 30 秒;若超過全域上限則自動套用上限。

  • 安全性
    不論位置或具名參數,都不會做字串拼接;在 CreateCommand() 期間會轉成 DbParameter,可有效防止 SQL Injection。


4️⃣ DbCommandSpec 使用範例

A. 查詢(位置參數)

string sql = @"
SELECT  EmployeeId,
        Name,
        HiredOn
FROM    Employee
WHERE   EmployeeId = {0}";

var command = new DbCommandSpec(DbCommandKind.DataTable, sql, 1001);
var db = new DbAccess("HR");
var employees = db.Query<Employee>(command); // List<Employee>

B. 查詢單一值(位置參數)

var spec = new DbCommandSpec(
    DbCommandKind.Scalar,
    "SELECT COUNT(*) FROM Employee WHERE DepartmentId = {0}",
    5
);

var db = new DbAccess("HR");
var result = db.Execute(spec);
int count = Convert.ToInt32(result.Scalar);

C. 新增資料(位置參數 + 逾時,多欄位)

var spec = new DbCommandSpec(
    DbCommandKind.NonQuery,
    "INSERT INTO Department(Name, CreatedOn, IsActive) VALUES({0}, {1}, {2})",
    "R&D", DateTime.Now, true
)
{
    CommandTimeout = 60 // <=0 使用預設 30 秒;超過全域上限則套用上限(由 setter 處理)
};

var db = new DbAccess("HR");
DbCommandResult r = db.Execute(spec);
Console.WriteLine($"Rows affected: {r.RowsAffected}");

D. 更新資料(具名參數,多欄位更新 + 條件)

var spec = new DbCommandSpec(
    DbCommandKind.NonQuery,
    "UPDATE Employee SET Name = {name}, HiredOn = {hiredOn} WHERE EmployeeId = {employeeId}",
    new Dictionary<string, object>
    {
        ["name"] = "Alice",
        ["hiredOn"] = new DateTime(2024, 1, 1),
        ["employeeId"] = 1001
    }
);

var db = new DbAccess("HR");
db.Execute(spec);

E. Stored Procedure(具名參數示範)

var spec = new DbCommandSpec(
    DbCommandKind.NonQuery,
    "usp_UpdateSalary",
    new Dictionary<string, object>
    {
        ["employeeId"] = 1001,
        ["delta"] = 1000m
    }
)
{
    CommandType = CommandType.StoredProcedure
};

var db = new DbAccess("HR");
db.Execute(spec);

5️⃣ 總結

透過這次的改版,DbAccess 已成為 BeeNET 中唯一且安全的資料存取入口:

  • 開發者可以只用單一類別完成大部分資料庫操作。
  • 連線與命令資源交由框架管理,降低資源洩漏風險。
  • 批次命令與交易更簡潔,失敗會自動回滾並回報失敗位置。
  • 可依需求選擇內部或外部連線模式,靈活整合其他框架。

開發者現在可以更專注於 SQL 與業務邏輯本身,讓資料存取更直觀、更可靠。


📢 歡迎轉載,請註明出處
📬 歡迎追蹤我的技術筆記與實戰經驗分享
FacebookHackMDGitHubNuGet


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言