首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

工廠模式與單例模式構建通用數據庫訪問層 (.NET 實戰)

     編輯:VC 教學

前言

企業級應用常面臨多種數據庫並存 (SQL Server, Oracle, MySQL, PostgreSQL) 及遷移需求。硬編碼特定 ADO.NET 提供程序 (SqlClient, OleDb, OracleClient) 會導致高耦合、難維護。本文展示如何結合 工廠模式 創建提供程序無關的訪問介面,並用 單例模式 實現連接池管理,構建靈活、高性能的數據訪問層 (DAL)。

一、統一訪問介面:抽象基類 DBOperator

定義核心操作契約,屏蔽底層差異:

public abstract class DBOperator {
    public abstract IDbConnection Connection { get; } // 底層連接對象
    public abstract void Open();                      // 打開連接
    public abstract void Close();                     // 關閉連接
    public abstract void BeginTrans();                // 開啟事務
    public abstract void CommitTrans();               // 提交事務
    public abstract void RollbackTrans();             // 回滾事務
    public abstract void ExecuteSql(string sql, string[] parameters, object[] values); // 執行非查詢
    public abstract DataSet ExecuteSqlForDataSet(string query); // 執行查詢返回 DataSet
}

為何用 abstract class 而非 interface? 後文單例連接池需在基類中提供靜態工廠方法 Instance,介面無法包含實現代碼。

二、具體實現類:針對性封裝

SQL Server 實現

internal class SqlDBOperator : DBOperator {
    private SqlConnection _conn;
    private SqlTransaction _trans;
    private bool _inTransaction;

    public SqlDBOperator(string connStr) => _conn = new SqlConnection(connStr);

    public override IDbConnection Connection => _conn;

    public override void Open() {
        if (_conn.State != ConnectionState.Open) _conn.Open();
    }
    public override void Close() {
        if (_conn.State == ConnectionState.Open) _conn.Close();
    }
    public override void BeginTrans() {
        _trans = _conn.BeginTransaction();
        _inTransaction = true;
    }
    public override void CommitTrans() { _trans.Commit(); _inTransaction = false; }
    public override void RollbackTrans() { _trans.Rollback(); _inTransaction = false; }

    public override void ExecuteSql(string sql, string[] parameters, object[] values) {
        using (var cmd = _conn.CreateCommand()) {
            cmd.CommandText = sql;
            if (_inTransaction) cmd.Transaction = _trans;
            if (parameters != null) {
                if (parameters.Length != values.Length) throw new ArgumentException("參數值不匹配");
                for (int i = 0; i < parameters.Length; i++)
                    cmd.Parameters.AddWithValue(parameters[i], values[i]);
            }
            cmd.ExecuteNonQuery();
        }
    }
    public override DataSet ExecuteSqlForDataSet(string query) {
        using (var cmd = _conn.CreateCommand()) {
            cmd.CommandText = query;
            if (_inTransaction) cmd.Transaction = _trans;
            var adapter = new SqlDataAdapter((SqlCommand)cmd);
            var ds = new DataSet();
            adapter.Fill(ds);
            return ds;
        }
    }
}

OleDb 實現 (關鍵差異:參數佔位符)

internal class OleDBOperator : DBOperator {
    // ... 結構同 SqlDBOperator,替換 SqlXxx 為 OleDbXxx ...

    public override void ExecuteSql(string sql, string[] parameters, object[] values) {
        using (var cmd = _conn.CreateCommand()) {
            cmd.CommandText = ConvertOleDbParameters(sql); // @param → ?
            // ... 其餘邏輯相同 ...
        }
    }
    private string ConvertOleDbParameters(string sql) {
        // 簡單替換邏輯,實際建議用正則表達式處理
        return sql.Replace("@", "?");
    }
}

三、工廠模式:自動選擇提供程序

獨立工廠類

public static class DBOperatorFactory {
    public static DBOperator GetDBOperator(string connectionString) {
        // 簡單判斷邏輯:OleDb 連接串通常包含 "Provider="
        if (connectionString.IndexOf("provider=", StringComparison.OrdinalIgnoreCase) < 0)
            return new SqlDBOperator(connectionString);
        else
            return new OleDBOperator(connectionString);
    }
}

客戶端調用 (零感知切換)

// 配置文件中切換連接串,代碼無需修改
string connStr = ConfigurationManager.ConnectionStrings["DefaultDB"].ConnectionString;

using (var db = DBOperatorFactory.GetDBOperator(connStr)) {
    db.Open();
    var ds = db.ExecuteSqlForDataSet("SELECT * FROM Users WHERE Role=@Role", 
        new[] {"@Role"}, new object[] {"Admin"});
    // ...
    db.Close();
}

// 事務模式
using (var db = DBOperatorFactory.GetDBOperator(connStr)) {
    db.Open();
    db.BeginTrans();
    try {
        db.ExecuteSql("INSERT ...", ...);
        db.ExecuteSql("UPDATE ...", ...);
        db.CommitTrans();
    } catch {
        db.RollbackTrans();
        throw;
    } finally {
        db.Close();
    }
}

自管理工廠 (基類靜態方法,省去工廠類)

public abstract class DBOperator {
    // ... 抽象成員 ...

    public static DBOperator Instance(string connectionString) {
        if (connectionString.IndexOf("provider=", StringComparison.OrdinalIgnoreCase) < 0)
            return new SqlDBOperator(connectionString);
        else
            return new OleDBOperator(connectionString);
    }
}

// 調用簡化為:
var db = DBOperator.Instance(connStr);

四、單例模式:自定義連接池管理

雖然 ADO.NET 內置連接池,但自管池可實現:精確控制連接數上限、連接生命週期監控、跨提供程序統一池化策略。

DBOperator 基類擴展

public abstract class DBOperator {
    // 靜態池字段
    private static DBOperator[] _pool;
    private static int _currentIndex = -1;
    private static int _maxPoolSize = 10; // 可從配置讀取

    static DBOperator() {
        // 從配置初始化池大小
        string cfg = ConfigurationManager.AppSettings["PoolCount"];
        if (int.TryParse(cfg, out int size) && size > 0) _maxPoolSize = size;
        _pool = new DBOperator[_maxPoolSize];
    }

    // 替換 Instance 方法
    public static DBOperator Instance(string connectionString) {
        if (_maxPoolSize < 1) return CreateNewOperator(connectionString); // 無池模式

        // 循環輪詢策略 (可替換為更高級算法: LRU, 最少連接等)
        _currentIndex = (_currentIndex + 1) % _maxPoolSize;
        if (_pool[_currentIndex] == null)
            _pool[_currentIndex] = CreateNewOperator(connectionString);
        
        // 可選:驗證連接有效性,失效則重建
        if (_pool[_currentIndex].Connection.State == ConnectionState.Broken)
            _pool[_currentIndex] = CreateNewOperator(connectionString);

        return _pool[_currentIndex];
    }

    private static DBOperator CreateNewOperator(string connStr) {
        if (connStr.IndexOf("provider=", StringComparison.OrdinalIgnoreCase) < 0)
            return new SqlDBOperator(connStr);
        return new OleDBOperator(connStr);
    }
    // ... 抽象成員保持不變 ...
}

池化策略優化建議

  • 連接驗證:借用前執行 SELECT 1 或 Connection.Ping (MySQL) 檢測存活。
  • 異步預熱:應用啟動時異步預創建核心連接數。
  • 統計監控:記錄借用/歸還耗時、池命中率、峰值併發,導出 Prometheus/InfluxDB。
  • 按連接串分池:實際生產中應以連接串為 Key 維護 Dictionary 多池隔離。

五、架構優勢總結

維度傳統硬編碼本文方案
數據庫遷移修改所有 DAL 代碼僅修改連接串配置
新增數據庫大量重構新增一個 Operator 類 + 更新工廠判斷邏輯
連接管理依賴 ADO.NET 黑盒池可視、可控、可擴展的自定義池
事務處理分散在各業務代碼統一 Begin/Commit/Rollback 介面
參數化查詢手動拼接 SQL 風險高統一 Parameters 集合防注入

結語

「工廠解耦創建,單例管控資源」是構建企業級 DAL 的經典組合拳。本方案在 Websharp 中間件生產環境驗證多年,穩定支撐 SQL Server/Oracle/PostgreSQL 混合部署場景。後續可引入 Dapper/EF Core 作為 Micro-ORM 增強映射能力,或集成 Polly 實現重試/熔斷韌性模式,演進為全功能數據訪問框架。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved