如何在多类间共享DbConn实例实现变量访问与数据库操作?
解决DbConn实例共享与代码解耦问题
问题背景
现有三个类:SettingsControl(UserControl)、Main(Form)、DbConn,需求如下:
- 让
SettingsControl设置DbConn中的数据库连接字符串,Main窗体能够读取该字符串 DbConn作为数据库操作中间件,统一管理连接凭证并处理所有数据库操作,实现代码解耦
当前核心问题:SettingsControl和Main各自实例化DbConn对象,导致两者无法共享连接字符串。需要实现类似全局实例的效果,同时保证代码符合规范并适配多线程场景。
解决方案
方案1:线程安全的单例模式(快速实现全局共享)
通过单例模式确保整个应用中只有一个DbConn实例,同时通过锁机制保证多线程下的读写安全。
修改DbConn类(线程安全单例)
namespace mySQL_Project { internal class DbConn { // 静态私有实例,volatile关键字保证多线程下的内存可见性 private static volatile DbConn _instance; // 锁对象,确保线程安全的实例创建与变量读写 private static readonly object _lockObj = new object(); private string _connString; // 私有构造函数,禁止外部直接实例化 private DbConn() {} // 获取全局唯一实例的公共属性 public static DbConn Instance { get { // 双重检查锁定,兼顾线程安全与性能 if (_instance == null) { lock (_lockObj) { if (_instance == null) { _instance = new DbConn(); } } } return _instance; } } // 设置SQLite连接字符串(加锁保证原子性) public void SetSQLiteConnString(string connectionString) { lock (_lockObj) { _connString = connectionString; } } // 获取SQLite连接字符串(加锁保证读取一致性) public string GetSQLiteConnString() { lock (_lockObj) { return _connString; } } // 数据库操作示例:查询苹果库存总量 public int GetSumApples() { // 每个数据库操作使用独立连接,避免多线程冲突 using (var conn = new SQLiteConnection(_connString)) { conn.Open(); using (var cmd = new SQLiteCommand(@"SELECT sum FROM inv WHERE ID=@AI", conn)) { cmd.Parameters.AddWithValue("@AI", "3"); var result = cmd.ExecuteScalar(); return result != null ? Convert.ToInt32(result) : 0; } } } } }
修改SettingsControl类(使用单例实例)
namespace mySQL_Project { public partial class SettingsControl : UserControl { public SettingsControl() { InitializeComponent(); } private void Button_SetDB_Click(object sender, EventArgs e) { // 修正:获取TextBox的Text属性 string connectString = tb_stringtext.Text; DbConn.Instance.SetSQLiteConnString(connectString); } } }
修改Main类(使用单例实例)
namespace mySQL_Project { public partial class Main : Form { public Main() { InitializeComponent(); } private void textBox_getDBConn_Click(object sender, EventArgs e) { label_dbinfo.Text = DbConn.Instance.GetSQLiteConnString(); } } }
方案2:依赖注入(推荐中大型项目,解耦性更强)
通过构造函数注入DbConn实例,避免单例带来的全局耦合,同时保证不同组件共享同一个实例,更符合现代软件工程规范。
修改DbConn类(普通类,保留线程安全读写)
namespace mySQL_Project { internal class DbConn { private string _connString; private readonly object _lockObj = new object(); public void SetSQLiteConnString(string connectionString) { lock (_lockObj) { _connString = connectionString; } } public string GetSQLiteConnString() { lock (_lockObj) { return _connString; } } public int GetSumApples() { using (var conn = new SQLiteConnection(_connString)) { conn.Open(); using (var cmd = new SQLiteCommand(@"SELECT sum FROM inv WHERE ID=@AI", conn)) { cmd.Parameters.AddWithValue("@AI", "3"); var result = cmd.ExecuteScalar(); return result != null ? Convert.ToInt32(result) : 0; } } } } }
程序入口(创建共享实例并注入)
namespace mySQL_Project { internal static class Program { [STAThread] static void Main() { ApplicationConfiguration.Initialize(); // 创建全局共享的DbConn实例 var dbConn = new DbConn(); // 将实例注入Main窗体 var mainForm = new Main(dbConn); // 若SettingsControl在Main中初始化,同步传递该实例 // var settingsControl = new SettingsControl(dbConn); Application.Run(mainForm); } } }
修改Main类(构造函数注入)
namespace mySQL_Project { public partial class Main : Form { private readonly DbConn _dbConn; // 通过构造函数接收注入的DbConn实例 public Main(DbConn dbConn) { _dbConn = dbConn; InitializeComponent(); // 初始化SettingsControl时传递实例 // settingsControl = new SettingsControl(_dbConn); } private void textBox_getDBConn_Click(object sender, EventArgs e) { label_dbinfo.Text = _dbConn.GetSQLiteConnString(); } } }
修改SettingsControl类(构造函数注入)
namespace mySQL_Project { public partial class SettingsControl : UserControl { private readonly DbConn _dbConn; // 通过构造函数接收注入的DbConn实例 public SettingsControl(DbConn dbConn) { _dbConn = dbConn; InitializeComponent(); } private void Button_SetDB_Click(object sender, EventArgs e) { string connectString = tb_stringtext.Text; _dbConn.SetSQLiteConnString(connectString); } } }
多线程场景考量
- 连接字符串读写:通过
lock锁保证读写操作的原子性,避免多线程下的脏读、脏写问题 - 数据库连接使用:每个数据库操作创建独立的连接(用
using自动释放),不要共享连接对象——SQLite连接本身并非线程安全,ADO.NET连接池会自动处理连接复用 - 单例线程安全:双重检查锁定+
volatile关键字,确保多线程环境下正确创建唯一实例
方案对比
- 单例模式:实现简单,适合小型项目快速落地,但会带来一定的代码耦合
- 依赖注入:解耦性更强,便于单元测试和功能扩展,符合现代软件工程规范,推荐中大型项目使用
内容的提问来源于stack exchange,提问作者ElektrischInkorrekt
相关产品推荐
相关产品推荐

