本類別庫封裝 ADO.NET SqlClient 操作,提供連線管理、命令執行、資料配接器、交易支援、自動更新等功能,採用IDisposable 模式確保資源確定性釋放。
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;
}
}
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();
}
}
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");
}
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);
}
}
}
| 議題 | 改進方案 |
|---|---|
| 硬編碼資料庫專用 | 抽象 `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),舊系統維護可參考此處的資源管理與錯誤處理模式。