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

C# 資料存取通用類別庫:ADO.NET 封裝、連線管理與交易支援

     編輯:關於C語言

架構概覽

本類別庫封裝 ADO.NET SqlClient 操作,提供連線管理、命令執行、資料配接器、交易支援、自動更新等功能,採用IDisposable 模式確保資源確定性釋放。

核心類別設計

1. SqlConnect:連線字串管理與建立


public class SqlConnect {
    static string sqlconnstr;

    public string ConnString { get; set; }

    public SqlConnection GetSqlConnection() {
        SqlConnection conn = null;
        if (string.IsNullOrEmpty(sqlconnstr)) {
            // 從外部 DLL 讀取設定(CBERP_Dis.DLL)
            string sysDir = Environment.SystemDirectory;
            var config = new DisposalConductService(sysDir + @"\CBERP_Dis.DLL");
            sqlconnstr = $"server={config.GetValue("ServerName","(local)")};" +
                         $"user id={config.GetValue("SqlAccount","sa")};" +
                         $"password={config.GetValue("PassWord","jiayu520")};" +
                         $"Database={config.GetValue("Database","CBERP")}";
        }
        try {
            conn = new SqlConnection(sqlconnstr);
            conn.Open();
        } catch (SqlException se) {
            // 錯誤號 17=找不到伺服器, 18456=登入失敗
            if (se.Number == 17 || se.Number == 18456) {
                if (MessageBox.Show("資料庫參數錯誤,是否設定?", "提示",
                    MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) {
                    sqlconnstr = "";
                    new DisposalConduct().ShowDialog();  // 設定視窗
                    return GetSqlConnection();  // 遞迴重試
                } else {
                    MessageBox.Show("無法連線,程式結束");
                    Application.Exit();
                }
            } else {
                MessageBox.Show(se.Message);
                conn?.Close();
            }
        }
        return conn;
    }
}

2. DataCatena:核心資料操作封裝(實作 IDisposable)


public class DataCatena : IDisposable {
    private SqlConnection _connection;

    public DataCatena() {
        _connection = new SqlConnect().GetSqlConnection();
        _connection.Close();  // 取得後關閉,由各方法自行開啟
    }

    public DataCatena(string connStr) {
        _connection = new SqlConnection(connStr);
    }

    // 取得 DataView
    public DataView GetDataView(string sql) {
        var da = new SqlDataAdapter(sql, _connection);
        var ds = new DataSet();
        try {
            da.Fill(ds, "table");
            return ds.Tables["table"].DefaultView;
        } catch (SqlException ex) {
            new DebugBox(sql + "\r\n" + ex.Message).Show();
            return null;
        }
    }

    // 取得 DataTable
    public DataTable GetDT(string sql) {
        var da = new SqlDataAdapter(sql, _connection);
        var ds = new DataSet();
        try {
            da.Fill(ds, "table");
            return ds.Tables["table"];
        } catch (SqlException ex) {
            new DebugBox(sql + "\r\n" + ex.ToString()).Show();
            return null;
        }
    }

    // 取得 DataAdapter
    public SqlDataAdapter GetSqlDA(string sql) =>
        new SqlDataAdapter(sql, _connection);

    // 執行標量查詢
    public object ExecWithValue(string sql) {
        object result = null;
        _connection.Open();
        try {
            using (var cmd = new SqlCommand(sql, _connection))
                result = cmd.ExecuteScalar();
        } catch (SqlException ex) {
            new DebugBox(sql + "\r\n" + ex.ToString()).Show();
        } finally {
            _connection.Close();
        }
        return result;
    }

    // 執行非查詢(增刪改),回傳影響列數或錯誤碼
    public int Exec(string sql) {
        int result = 0;
        using (var cmd = new SqlCommand(sql, _connection)) {
            _connection.Open();
            try {
                cmd.ExecuteNonQuery();
            } catch (SqlException ex) {
                new DebugBox(sql + "\r\n" + ex.ToString()).Show();
                result = ex.Number;
            } finally {
                _connection.Close();
            }
        }
        return result;
    }

    // IDisposable 模式
    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing) {
        if (!disposing) return;
        _connection?.Dispose();
    }
}

3. SingleTable:單表離線操作


public class SingleTable {
    private DataCatena _dc;
    private SqlDataAdapter _da;
    private DataSet _ds = new DataSet();

    public SingleTable(string sql) {
        _dc = new DataCatena();
        _da = _dc.GetSqlDA(sql);
        _da.Fill(_ds, "Table1");
    }

    public void SetUpdateCommand(string sql) {
        _da.UpdateCommand = new SqlCommand(sql, _da.SelectCommand.Connection);
    }

    public DataTable GetDataTable() => _ds.Tables["Table1"];
    public SqlDataAdapter GetDataAdapter() => _da;

    public void Update() => _da.Update(_ds, "Table1");
}

4. AutoUpdateObj:CommandBuilder 自動生成更新命令


public class AutoUpdateObj {
    private DataCatena _dc;
    private SqlDataAdapter _da;
    private SqlCommandBuilder _cb;
    private DataSet _ds = new DataSet();

    public AutoUpdateObj(string sql) {
        _dc = new DataCatena();
        _da = _dc.GetSqlDA(sql);
        _cb = new SqlCommandBuilder(_da);  // 自動生成 INSERT/UPDATE/DELETE
        _da.Fill(_ds, "Table1");
    }

    public void SetSelectCommand(string sql) {
        _da.SelectCommand = new SqlCommand(sql, _da.SelectCommand.Connection);
        _cb.DataAdapter = _da;
        _ds.Tables.Clear();
    }

    public DataTable GetDataTable() {
        _ds.Clear();
        _da.Fill(_ds, "Table1");
        return _ds.Tables["Table1"];
    }

    public void Update() {
        try {
            _da.Update(_ds, "Table1");
        } catch (SqlException ex) {
            MessageBox.Show("更新失敗:" + ex.Message);
        }
    }
}

關鍵設計模式分析

連線管理策略

  • 短連線模式:每次操作開啟→執行→關閉,適合 Web/高併發
  • 連線共用:`DataCatena` 建構子開啟測試連線後立即關閉,實際操作由各方法自行管理
  • 外部設定載入:支援從 DLL/登錄檔/設定檔動態讀取連線字串

資源釋放模式

  • `DataCatena` 實作標準 `IDisposable` + `Dispose(bool)` 模式
  • `SqlCommand`、`SqlDataAdapter` 使用 `using` 確保釋放
  • `SqlConnection` 由 `DataCatena` 擁有,在 `Dispose` 時釋放

錯誤處理策略

  • 統一捕獲 `SqlException`
  • 顯示 `DebugBox`(自訂診斷視窗)含 SQL 與錯誤詳情
  • 關鍵錯誤(連線失敗)彈出 `MessageBox` 引導使用者設定

現代化改進建議

議題改進方案
硬編碼資料庫專用抽象 `IDbConnection`/`IDbCommand` 支援多資料庫
同步阻塞 API提供 `async/await` 版本(`ExecuteScalarAsync` 等)
SQL 注入風險強制參數化查詢,移除字串拼接 SQL
DataSet 記憶體佔用大大量資料改用 `SqlDataReader` 串流處理
緊耦合 WinForms (MessageBox)抽象 `ILogger`/`INotifier` 介面
無連線池控制連線字串加入 `Pooling=true;Max Pool Size=100`
交易支援不完整加入 `BeginTransaction`/`Commit`/`Rollback` 封裝

使用範例


// 簡單查詢
using (var dc = new DataCatena()) {
    var view = dc.GetDataView("SELECT * FROM Users WHERE Active=1");
    foreach (DataRowView row in view) {
        Console.WriteLine(row["UserName"]);
    }
}

// 離線編輯後批次更新
var table = new SingleTable("SELECT * FROM Products");
var dt = table.GetDataTable();
// ... 修改 dt.Rows ...
table.SetUpdateCommand("UPDATE Products SET Name=@Name, Price=@Price WHERE Id=@Id");
table.Update();

// 自動更新(需 Primary Key)
var auto = new AutoUpdateObj("SELECT * FROM Orders");
var dt2 = auto.GetDataTable();
// ... 修改 ...
auto.Update();

總結

此類別庫展示了 ADO.NET 經典封裝模式:連線管理、命令執行、離線資料集操作、自動命令生成。雖架構經典但缺乏現代非同步、參數化、抽象化特性。新專案建議採用 Dapper(輕量 Micro-ORM)或 Entity Framework Core(完整 ORM),舊系統維護可參考此處的資源管理與錯誤處理模式。

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