MySQL 数据库单例模式在 WinForm 中的应用

 一、基础 MySQL 单例实现

1.1 最简单的 MySQL 连接单例

csharp
using MySql.Data.MySqlClient;

public sealed class MySqlDb
{
    private static MySqlDb _instance;
    private static readonly object _lock = new object();
    
    private MySqlConnection _connection;
    private string _connectionString;
    
    private MySqlDb()
    {
        _connectionString = "Server=localhost;Database=mydb;Uid=root;Pwd=123456;";
        _connection = new MySqlConnection(_connectionString);
    }
    
    public static MySqlDb Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (_lock)
                {
                    if (_instance == null)
                    {
                        _instance = new MySqlDb();
                    }
                }
            }
            return _instance;
        }
    }
    
    public MySqlConnection GetConnection()
    {
        if (_connection.State != ConnectionState.Open)
        {
            _connection.Open();
        }
        return _connection;
    }
}
 1.2 使用示例

csharp
// 获取连接
var connection = MySqlDb.Instance.GetConnection();

// 执行查询
string sql = "SELECT * FROM users WHERE status=1";
using (var cmd = new MySqlCommand(sql, connection))
{
    using (var reader = cmd.ExecuteReader())
    {
        while (reader.Read())
        {
            string username = reader["username"].ToString();
            // 在 WinForm 控件显示
            listBoxUsers.Items.Add(username);
        }
    }
}

二、WinForm 专用 MySQL 单例类

### 2.1 带错误处理和 UI 更新的实现

```csharp
public sealed class WinFormMySqlDb
{
    private static readonly Lazy<WinFormMySqlDb> _instance = 
        new Lazy<WinFormMySqlDb>(() => new WinFormMySqlDb());
    
    public static WinFormMySqlDb Instance => _instance.Value;
    
    private MySqlConnection _connection;
    private readonly string _connString;
    
    private WinFormMySqlDb()
    {
        _connString = ConfigurationManager.ConnectionStrings["MySqlDB"].ConnectionString;
        _connection = new MySqlConnection(_connString);
    }
    
    public void ExecuteQuery(string sql, Action<MySqlDataReader> processResult, Control uiControl = null)
    {
        try
        {
            EnsureConnectionOpen();
            
            using (var cmd = new MySqlCommand(sql, _connection))
            {
                using (var reader = cmd.ExecuteReader())
                {
                    if (uiControl != null && uiControl.InvokeRequired)
                    {
                        uiControl.Invoke(new Action(() => processResult(reader)));
                    }
                    else
                    {
                        processResult(reader);
                    }
                }
            }
        }
        catch (Exception ex)
        {
            ShowError(ex.Message, uiControl);
        }
    }
    
    private void EnsureConnectionOpen()
    {
        if (_connection.State != ConnectionState.Open)
        {
            _connection.Open();
        }
    }
    
    private void ShowError(string message, Control uiControl)
    {
        if (uiControl != null)
        {
            if (uiControl.InvokeRequired)
            {
                uiControl.Invoke(new Action(() => 
                    MessageBox.Show(uiControl, message, "数据库错误")));
            }
            else
            {
                MessageBox.Show(uiControl, message, "数据库错误");
            }
        }
        else
        {
            MessageBox.Show(message, "数据库错误");
        }
    }
    
    public void CloseConnection()
    {
        if (_connection.State == ConnectionState.Open)
        {
            _connection.Close();
        }
    }
}
2.2 使用示例

csharp
// 在按钮点击事件中使用
private void btnLoadUsers_Click(object sender, EventArgs e)
{
    string sql = "SELECT id, username FROM users";
    
    WinFormMySqlDb.Instance.ExecuteQuery(sql, reader => 
    {
        dataGridViewUsers.Rows.Clear();
        while (reader.Read())
        {
            dataGridViewUsers.Rows.Add(
                reader["id"].ToString(),
                reader["username"].ToString());
        }
    }, this);
}
 三、MySQL 连接池单例实现

3.1 适合 WinForm 的连接池单例

csharp
public sealed class MySqlConnectionPool
{
    private static readonly Lazy<MySqlConnectionPool> _instance = 
        new Lazy<MySqlConnectionPool>(() => new MySqlConnectionPool());
    
    public static MySqlConnectionPool Instance => _instance.Value;
    
    private readonly ConcurrentBag<MySqlConnection> _pool = new ConcurrentBag<MySqlConnection>();
    private readonly string _connectionString;
    private readonly int _maxSize = 10;
    private int _currentCount = 0;
    private readonly object _lock = new object();
    
    private MySqlConnectionPool()
    {
        _connectionString = "Server=localhost;Database=mydb;Uid=root;Pwd=123456;Pooling=true;";
        InitializePool(3); // 初始化 3 个连接
    }
    
    private void InitializePool(int initialCount)
    {
        for (int i = 0; i < initialCount && _currentCount < _maxSize; i++)
        {
            _pool.Add(CreateNewConnection());
        }
    }
    
    private MySqlConnection CreateNewConnection()
    {
        lock (_lock)
        {
            if (_currentCount >= _maxSize)
                throw new Exception("连接池已满");
            
            var conn = new MySqlConnection(_connectionString);
            conn.Open();
            _currentCount++;
            return conn;
        }
    }
    
    public MySqlConnection GetConnection()
    {
        if (_pool.TryTake(out var conn))
        {
            if (conn.Ping()) // 检查连接是否有效
                return conn;
            
            conn.Dispose();
            lock (_lock) { _currentCount--; }
        }
        return CreateNewConnection();
    }
    
    public void ReleaseConnection(MySqlConnection conn)
    {
        if (conn == null) return;
        
        if (!conn.Ping()) // 检查连接是否有效
        {
            conn.Dispose();
            lock (_lock) { _currentCount--; }
            return;
        }
        
        _pool.Add(conn);
    }
    
    public void Cleanup()
    {
        while (_pool.TryTake(out var conn))
        {
            conn.Dispose();
            lock (_lock) { _currentCount--; }
        }
    }
}
 3.2 使用示例

csharp
// 获取连接
var connection = MySqlConnectionPool.Instance.GetConnection();

try
{
    // 执行操作
    string sql = "UPDATE products SET stock = stock - 1 WHERE id = @id";
    using (var cmd = new MySqlCommand(sql, connection))
    {
        cmd.Parameters.AddWithValue("@id", productId);
        int affected = cmd.ExecuteNonQuery();
        
        // 更新 UI
        this.Invoke((MethodInvoker)delegate {
            lblStatus.Text = affected > 0 ? "更新成功" : "更新失败";
        });
    }
}
finally
{
    // 释放连接
    MySqlConnectionPool.Instance.ReleaseConnection(connection);
}
四、WinForm 中推荐的 MySQL 单例用法

4.1 最佳实践实现

csharp
public sealed class AppMySqlDb : IDisposable
{
    private static readonly Lazy<AppMySqlDb> _instance = 
        new Lazy<AppMySqlDb>(() => new AppMySqlDb());
    
    public static AppMySqlDb Instance => _instance.Value;
    
    private MySqlConnection _connection;
    private readonly string _connString;
    
    private AppMySqlDb()
    {
        _connString = BuildConnectionString();
        _connection = new MySqlConnection(_connString);
    }
    
    private string BuildConnectionString()
    {
        var builder = new MySqlConnectionStringBuilder
        {
            Server = "localhost",
            Database = "myappdb",
            UserID = "appuser",
            Password = "securepwd",
            Pooling = true,
            MinimumPoolSize = 1,
            MaximumPoolSize = 5,
            ConnectionTimeout = 15,
            CharacterSet = "utf8mb4"
        };
        return builder.ToString();
    }
    
    public DataTable GetDataTable(string sql, params MySqlParameter[] parameters)
    {
        var dt = new DataTable();
        
        try
        {
            using (var cmd = new MySqlCommand(sql, _connection))
            {
                if (parameters != null)
                    cmd.Parameters.AddRange(parameters);
                
                _connection.Open();
                using (var adapter = new MySqlDataAdapter(cmd))
                {
                    adapter.Fill(dt);
                }
            }
        }
        catch (Exception ex)
        {
            LogError(ex);
            throw;
        }
        finally
        {
            if (_connection.State == ConnectionState.Open)
                _connection.Close();
        }
        
        return dt;
    }
    
    public int ExecuteNonQuery(string sql, params MySqlParameter[] parameters)
    {
        try
        {
            using (var cmd = new MySqlCommand(sql, _connection))
            {
                if (parameters != null)
                    cmd.Parameters.AddRange(parameters);
                
                _connection.Open();
                return cmd.ExecuteNonQuery();
            }
        }
        catch (Exception ex)
        {
            LogError(ex);
            throw;
        }
        finally
        {
            if (_connection.State == ConnectionState.Open)
                _connection.Close();
        }
    }
    
    private void LogError(Exception ex)
    {
        // 可以记录到文件或显示在 UI 上
        Debug.WriteLine($"[MySQL Error] {ex.Message}");
    }
    
    public void Dispose()
    {
        _connection?.Dispose();
    }
}
```

4.2 在 WinForm 中的典型使用

csharp
// 绑定 DataGridView
private void LoadCustomerData()
{
    try
    {
        string sql = "SELECT id, name, phone FROM customers WHERE active=1";
        DataTable dt = AppMySqlDb.Instance.GetDataTable(sql);
        
        dataGridViewCustomers.Invoke((MethodInvoker)delegate {
            dataGridViewCustomers.DataSource = dt;
            dataGridViewCustomers.AutoResizeColumns();
        });
    }
    catch (Exception ex)
    {
        MessageBox.Show(this, $"加载数据失败: {ex.Message}", "错误", 
            MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

// 执行更新操作
private void btnUpdate_Click(object sender, EventArgs e)
{
    string sql = "UPDATE products SET price=@price WHERE id=@id";
    
    var parameters = new[]
    {
        new MySqlParameter("@price", decimal.Parse(txtPrice.Text)),
        new MySqlParameter("@id", int.Parse(txtProductId.Text))
    };
    
    try
    {
        int rows = AppMySqlDb.Instance.ExecuteNonQuery(sql, parameters);
        MessageBox.Show($"成功更新 {rows} 条记录");
    }
    catch (Exception ex)
    {
        MessageBox.Show($"更新失败: {ex.Message}");
    }
}
 五、关键注意事项

1. **连接管理**:
   - 及时关闭连接(使用 using 语句或手动 Close)
   - 避免长期保持连接打开

2. **线程安全**:
   - UI 操作必须通过 Invoke/BeginInvoke
   - 数据库操作不要在 UI 线程执行耗时查询

3. **异常处理**:
   - 捕获 MySqlException 特定异常
   - 提供用户友好的错误信息

4. **配置最佳实践**:
   - 连接字符串放在配置文件中
   - 敏感信息加密处理
   - 设置合理的连接超时和池大小

5. **释放资源**:
   - 应用程序退出时释放所有数据库资源
   - 实现 IDisposable 接口

这种MySQL 单例实现特别适合 WinForm 应用程序,既保证了数据库访问的高效性,又考虑了 WinForm 特有的 UI 线程安全问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值