一、基础 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 线程安全问题。