| 層級 | 職責 | 關鍵點 |
|---|---|---|
| BLL (Business Logic Layer) | 開啟/提交/回滾交易、協調多個 DAL 操作 | using (SqlTransaction tran = SqlHelper.BeginTransaction()) |
| DAL (Data Access Layer) | 執行具體 SQL,接收外部傳入的 SqlTransaction | 方法簽名加 SqlTransaction tran 參數 |
| SqlHelper | 管理共用 SqlConnection、提供 BeginTransaction/CloseConnection | 單一連線實例贯穿交易週期 |
public static class SqlHelper
{
private static readonly string ConnStr = ConfigurationManager.ConnectionStrings["Default"].ConnectionString;
private static SqlConnection _conn = new SqlConnection(ConnStr);
private static SqlConnection GetConnection()
{
if (_conn.State == ConnectionState.Closed) _conn.Open();
return _conn;
}
public static SqlTransaction BeginTransaction()
{
return GetConnection().BeginTransaction();
}
public static void CloseConnection()
{
if (_conn.State != ConnectionState.Closed) _conn.Close();
}
// 多載 ExecuteNonQuery 接收 transaction
public static int ExecuteNonQuery(string sql, SqlParameter[] parameters, CommandType type, SqlTransaction tran)
{
using (SqlCommand cmd = new SqlCommand(sql, (SqlConnection)tran.Connection, tran))
{
cmd.CommandType = type;
if (parameters != null) cmd.Parameters.AddRange(parameters);
return cmd.ExecuteNonQuery();
}
}
}
public class UserDAO
{
public bool InsertTest(int value, SqlTransaction tran)
{
string sql = "INSERT INTO [TestTable]([Item]) VALUES(@val)";
SqlParameter[] paras = { new SqlParameter("@val", value) };
return SqlHelper.ExecuteNonQuery(sql, paras, CommandType.Text, tran) > 0;
}
}
public class UserService
{
private readonly UserDAO _dao = new UserDAO();
public bool BatchInsert()
{
using (SqlTransaction tran = SqlHelper.BeginTransaction())
{
try
{
_dao.InsertTest(2, tran);
_dao.InsertTest(3, tran);
// 可繼續呼叫其他 DAL 方法,共用同一 tran
tran.Commit();
return true;
}
catch (Exception ex)
{
tran.Rollback();
// 記錄 Log
return false;
}
finally
{
SqlHelper.CloseConnection(); // 歸還連線
}
}
}
}
SqlHelper._conn 為 static 單例,交易期間保持開啟,所有命令共用此連線。SqlTransaction,透過參數傳給 DAL,DAL 建立 SqlCommand 時指定 cmd.Transaction = tran。catch 區塊呼叫 tran.Rollback(),確保原子性。finally 呼叫 SqlHelper.CloseConnection(),避免連線洩漏。SqlConnection 非執行緒安全,僅適用單執行緒請求模型(傳統 WebForms 同步請求)。高併發或非同步環境必須改用 using (var conn = new SqlConnection(...)) 每請求一連線。// 介面
public interface IUnitOfWork : IDisposable
{
SqlTransaction BeginTransaction();
void Commit();
void Rollback();
}
// 實作(每請求一實例,由 DI 容器管理生命週期)
public class AdoUnitOfWork : IUnitOfWork
{
private readonly SqlConnection _conn;
private SqlTransaction _tran;
public AdoUnitOfWork(string connStr) { _conn = new SqlConnection(connStr); _conn.Open(); }
public SqlTransaction BeginTransaction() => _tran = _conn.BeginTransaction();
public void Commit() => _tran?.Commit();
public void Rollback() => _tran?.Rollback();
public void Dispose() { _tran?.Dispose(); _conn?.Dispose(); }
}
// BLL 使用
public class UserService
{
private readonly UserDAO _dao;
private readonly IUnitOfWork _uow;
public UserService(UserDAO dao, IUnitOfWork uow) { _dao = dao; _uow = uow; }
public bool BatchInsert()
{
var tran = _uow.BeginTransaction();
try {
_dao.InsertTest(2, tran);
_dao.InsertTest(3, tran);
_uow.Commit(); return true;
} catch { _uow.Rollback(); return false; }
}
}
優點:可測試、執行緒安全、支援非同步、解耦具體 ADO.NET 實作。
傳統三層架構中,BLL 掌控交易邊界、DAL 專注 SQL 執行、SqlHelper 提供共用連線 是經典模式。現代開發建議導入 Unit of Work + DI,獲得更好的可維護性與擴展性。