如何将数据库操作代码抽离为独立类实现多窗体复用
如何将数据库操作代码抽离为独立类实现多窗体复用
我完全懂你的痛点——在每个窗体里重复写相同的数据库连接、增删改查代码,不仅冗余,后期要改个连接字符串或者调整查询逻辑时,得一个个窗体去改,简直是噩梦!下面我给你一套实用的实现方案,把所有数据库操作封装到一个独立类里,各个窗体直接调用就行,彻底解决重复代码的问题。
核心思路
我们要做的就是把数据库相关的逻辑(连接、查询、增改删)全部放到一个专门的类里,比如叫DatabaseHelper,这个类对外只暴露简单的方法(比如获取数据、保存数据),内部处理连接的创建、资源释放这些细节。这样所有窗体都可以复用这个类,不用再重复写底层逻辑。
代码实现示例
这里以C# WinForms + SQL Server为例,其他数据库(比如MySQL、SQLite)只需要替换对应的数据库客户端类即可(比如把SqlConnection换成MySqlConnection)。
第一步:创建DatabaseHelper类
这个类是核心,负责所有数据库操作的封装:
using System.Data; using System.Data.SqlClient; using System.Configuration; // 要引用System.Configuration程序集,用来读配置文件 public class DatabaseHelper { // 从配置文件读取连接字符串(推荐,不要硬编码) private readonly string _connectionString = ConfigurationManager.ConnectionStrings["MyDatabase"].ConnectionString; // 私有方法:获取数据库连接,内部复用 private SqlConnection GetDatabaseConnection() { return new SqlConnection(_connectionString); } /// <summary> /// 查询数据,返回DataTable(方便绑定到DataGridView、ComboBox等控件) /// </summary> /// <param name="sqlQuery">SQL查询语句</param> /// <param name="parameters">参数数组(防止SQL注入)</param> /// <returns>查询结果DataTable</returns> public DataTable RetrieveData(string sqlQuery, SqlParameter[] parameters = null) { var resultTable = new DataTable(); // 使用using语句自动释放连接资源,避免内存泄漏 using (var conn = GetDatabaseConnection()) { using (var cmd = new SqlCommand(sqlQuery, conn)) { // 添加参数(如果有的话) if (parameters != null) { cmd.Parameters.AddRange(parameters); } conn.Open(); // 用DataAdapter填充DataTable using (var adapter = new SqlDataAdapter(cmd)) { adapter.Fill(resultTable); } } } return resultTable; } /// <summary> /// 执行增、删、改操作,返回受影响的行数 /// </summary> /// <param name="sqlQuery">SQL语句</param> /// <param name="parameters">参数数组</param> /// <returns>受影响的行数</returns> public int ExecuteModifyOperation(string sqlQuery, SqlParameter[] parameters = null) { int affectedRows = 0; using (var conn = GetDatabaseConnection()) { using (var cmd = new SqlCommand(sqlQuery, conn)) { if (parameters != null) { cmd.Parameters.AddRange(parameters); } conn.Open(); affectedRows = cmd.ExecuteNonQuery(); } } return affectedRows; } }
第二步:在App.config里配置连接字符串
把连接字符串放到配置文件里,后期修改更方便:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <connectionStrings> <!-- 替换成你的数据库连接字符串 --> <add name="MyDatabase" connectionString="Server=你的服务器地址;Database=你的数据库名;Integrated Security=True;" providerName="System.Data.SqlClient"/> </connectionStrings> </configuration>
第三步:主窗体调用示例
在主窗体里,只需要实例化DatabaseHelper,然后调用对应的方法就行:
public partial class MainForm : Form { // 全局实例化DatabaseHelper,整个窗体复用 private readonly DatabaseHelper _dbHelper = new DatabaseHelper(); public MainForm() { InitializeComponent(); } // 示例:根据用户输入查询数据并显示到DataGridView private void btnLoadData_Click(object sender, EventArgs e) { // 获取用户输入的筛选条件 string userFilter = txtSearch.Text; // 参数化查询,防止SQL注入 string query = "SELECT * FROM Products WHERE ProductName LIKE @Filter"; // 构建参数 var parameters = new SqlParameter[] { new SqlParameter("@Filter", $"%{userFilter}%") }; // 调用查询方法 var productData = _dbHelper.RetrieveData(query, parameters); // 绑定到DataGridView显示 dgvProducts.DataSource = productData; } // 示例:保存/修改数据 private void btnSaveProduct_Click(object sender, EventArgs e) { // 示例:插入新商品 string query = "INSERT INTO Products (ProductName, Price) VALUES (@Name, @Price)"; var parameters = new SqlParameter[] { new SqlParameter("@Name", txtProductName.Text), new SqlParameter("@Price", decimal.Parse(txtPrice.Text)) }; // 执行插入操作 int rowsAffected = _dbHelper.ExecuteModifyOperation(query, parameters); if (rowsAffected > 0) { MessageBox.Show("商品保存成功!"); // 刷新数据 btnLoadData_Click(sender, e); } else { MessageBox.Show("保存失败,请重试!"); } } }
第四步:其他窗体复用
其他窗体的用法和主窗体完全一样,只需要实例化DatabaseHelper,然后调用需要的方法:
public partial class UserManagementForm : Form { private readonly DatabaseHelper _dbHelper = new DatabaseHelper(); private void btnGetUsers_Click(object sender, EventArgs e) { string query = "SELECT UserId, UserName FROM Users"; var userData = _dbHelper.RetrieveData(query); // 绑定到ComboBox cboUsers.DataSource = userData; cboUsers.DisplayMember = "UserName"; cboUsers.ValueMember = "UserId"; } }
一些重要的注意事项
- SQL注入防护:一定要用参数化查询,绝对不要直接把用户输入拼接到SQL语句里,上面的示例已经严格遵守了这一点。
- 资源释放:所有数据库相关的对象(连接、命令、适配器)都用
using包裹,确保使用后自动释放资源,避免内存泄漏。 - 错误处理:可以根据需求在
DatabaseHelper里添加try-catch块,或者让调用窗体处理异常。比如在RetrieveData里捕获异常并返回null,或者抛出异常让调用者处理。 - 扩展性:如果后期需要支持多种数据库,可以把
DatabaseHelper做成抽象类,然后针对不同数据库实现子类,或者使用依赖注入来切换数据库。
备注:内容来源于stack exchange,提问作者troy strunks
相关产品推荐
相关产品推荐

