using (var conn = new SQLiteConnection(connStr))
{
conn.Open();
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = @"
BEGIN TRANSACTION;
INSERT INTO Users (Name, Email) VALUES ('Alice', 'alice@example.com');
INSERT INTO Users (Name, Email) VALUES ('Bob', 'bob@example.com');
UPDATE Accounts SET Balance = Balance - 100 WHERE UserId = 1;
UPDATE Accounts SET Balance = Balance + 100 WHERE UserId = 2;
COMMIT;
";
cmd.ExecuteNonQuery();
}
}
using System.Data;
using System.Data.SQLite;
public void Transfer(int fromUserId, int toUserId, decimal amount)
{
using (var conn = new SQLiteConnection(GetConnectionString()))
{
conn.Open();
using (var txn = conn.BeginTransaction())
{
try
{
using (var cmd = conn.CreateCommand())
{
cmd.Transaction = txn; // 關鍵:關聯交易
// 扣款
cmd.CommandText = "UPDATE Accounts SET Balance = Balance - @amt WHERE UserId = @id";
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("@amt", amount);
cmd.Parameters.AddWithValue("@id", fromUserId);
int rows1 = cmd.ExecuteNonQuery();
if (rows1 == 0) throw new Exception($"扣款失敗: UserId={fromUserId}");
// 入款
cmd.CommandText = "UPDATE Accounts SET Balance = Balance + @amt WHERE UserId = @id";
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("@amt", amount);
cmd.Parameters.AddWithValue("@id", toUserId);
int rows2 = cmd.ExecuteNonQuery();
if (rows2 == 0) throw new Exception($"入款失敗: UserId={toUserId}");
// 記錄流水
cmd.CommandText = @"INSERT INTO Transfers (FromUser, ToUser, Amount, CreatedAt)
VALUES (@from, @to, @amt, @now)";
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("@from", fromUserId);
cmd.Parameters.AddWithValue("@to", toUserId);
cmd.Parameters.AddWithValue("@amt", amount);
cmd.Parameters.AddWithValue("@now", DateTime.Now);
cmd.ExecuteNonQuery();
}
txn.Commit(); // 成功提交
}
catch (Exception ex)
{
txn.Rollback(); // 失敗回滾
// 記錄日誌、拋出或轉換例外
throw new TransferException("轉帳失敗", ex);
}
}
}
}
| 項目 | 說明 |
|---|---|
| BeginTransaction() | 回傳 DbTransaction (SQLite 為 SQLiteTransaction) |
| Command.Transaction | 每個 Command 必須指派 Transaction 屬性才參與交易 |
| 參數化查詢 | 防 SQL 注入、型別安全、效能佳 (計畫快取) |
| Commit/Rollback | 成對出現,使用 try/catch/finally 確保 |
| 隔離層級 | SQLite 預設 SERIALIZABLE (最高),可用 BeginTransaction(IsolationLevel) 指定 |
| 巢狀交易 | SQLite 不支援巢狀交易,嘗試會拋出例外 |
public static class SQLiteHelper
{
private static string _connStr = "Data Source=app.db;Version=3;";
public static string GetConnectionString() => _connStr;
public static SQLiteConnection GetConnection()
{
var conn = new SQLiteConnection(_connStr);
conn.Open();
return conn;
}
// 交易範本方法
public static void ExecuteInTransaction(Action action)
{
using (var conn = GetConnection())
using (var txn = conn.BeginTransaction())
{
try
{
action(conn, txn);
txn.Commit();
}
catch
{
txn.Rollback();
throw;
}
}
}
}
// 使用
SQLiteHelper.ExecuteInTransaction((conn, txn) => {
using (var cmd = conn.CreateCommand())
{
cmd.Transaction = txn;
cmd.CommandText = "INSERT INTO Log (Msg) VALUES (@msg)";
cmd.Parameters.AddWithValue("@msg", "測試");
cmd.ExecuteNonQuery();
}
});
PRAGMA journal_mode = WAL;:啟用 Write-Ahead Logging,大幅提升併發讀寫效能PRAGMA synchronous = NORMAL;:平衡安全與速度 (預設 FULL 最安全但較慢)SQLiteCommand 物件,透過 Parameters.Clear() 重設參數cmd.Transaction = txn → 命令不在交易中,自動提交Rollback() → 交易殘留,資料庫鎖定SQLiteTransaction → 設計困難,建議用委派/範本方法封裝正確使用 ADO.NET 交易 API,配合參數化查詢與例外處理,是 C# 操作 SQLite 進行可靠資料操作的標準模式。