1. 从零开始的MySQL连接器配置
作为.NET开发者,我们经常需要与MySQL数据库打交道。MySqlConnector作为ADO.NET的官方驱动程序,比传统的MySql.Data库具有更好的性能和异步支持。先通过NuGet安装它:
Install-Package MySqlConnector -Version 2.3.0
创建基础连接模板:
using MySqlConnector;
string connectionString = "Server=localhost;Database=testdb;User=root;Password=123456;";
using var connection = new MySqlConnection(connectionString);
try
{
await connection.OpenAsync();
Console.WriteLine("数据库连接成功!");
}
catch (MySqlException ex)
{
Console.WriteLine($"连接失败:错误代码 {ex.Number}");
}
这个模板包含了现代C#开发的关键要素:异步操作、资源自动释放和错误处理。注意using语句确保连接自动关闭,即使发生异常也能正确释放资源。
2. 基础查询的三种姿势
2.1 简单查询执行
最基础的查询方式适合快速验证数据:
string sql = "SELECT product_id, product_name FROM products WHERE price > 50";
using var command = new MySqlCommand(sql, connection);
using var reader = await command.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
Console.WriteLine($"ID:{reader["product_id"]} 名称:{reader["product_name"]}");
}
这种模式适合简单查询,但要注意字符串拼接可能存在SQL注入风险,后续我们会解决这个问题。
2.2 参数化查询实践
安全查询的黄金法则:
string searchTerm = "%手机%";
decimal minPrice = 1000;
var sql = @"
SELECT *
FROM products
WHERE product_name LIKE @keyword
AND price >= @minPrice
ORDER BY create_time DESC
LIMIT 100";
using var command = new MySqlCommand(sql, connection);
command.Parameters.AddWithValue("@keyword", searchTerm);
command.Parameters.AddWithValue("@minPrice", minPrice);
// 使用强类型读取方式
while (await reader.ReadAsync())
{
int id = reader.GetInt32("product_id");
string name = reader.GetString("product_name");
// 其他字段处理...
}
参数化查询不仅防止SQL注入,还能提升查询计划重用率。注意使用GetXXX方法可以避免装箱操作,提升性能。
2.3 存储过程调用
复杂业务逻辑的最佳拍档:
using var command = new MySqlCommand("CalculateOrderTotal", connection);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("@orderId", 1001);
command.Parameters.Add("@totalAmount", MySqlDbType.Decimal);
command.Parameters["@totalAmount"].Direction = ParameterDirection.Output;
await command.ExecuteNonQueryAsync();
decimal total = (decimal)command.Parameters["@totalAmount"].Value;
Console.WriteLine($"订单总金额:{total:C}");
存储过程特别适合需要数据库端计算的场景,注意输出参数的类型声明和方向设置。
3. 高级查询技巧实战
3.1 分页查询优化
高效分页是系统性能的关键:
int pageSize = 20;
int pageNumber = 3;
string baseSql = @"
SELECT SQL_CALC_FOUND_ROWS *
FROM user_logs
WHERE log_type = 'payment'
ORDER BY create_time DESC
LIMIT @offset, @pageSize;
SELECT FOUND_ROWS() AS totalRecords;";
using var command = new MySqlCommand(baseSql, connection);
command.Parameters.AddWithValue("@pageSize", pageSize);
command.Parameters.AddWithValue("@offset", (pageNumber - 1) * pageSize);
using var reader = await command.ExecuteReaderAsync();
// 读取第一页数据
var logs = new List<UserLog>();
while (await reader.ReadAsync())
{
// 数据转换逻辑...
}
// 读取总记录数
await reader.NextResultAsync();
await reader.ReadAsync();
int total = reader.GetInt32("totalRecords");
这里使用了SQL_CALC_FOUND_ROWS技巧,在单次查询中同时获取分页数据和总记录数,比传统两次查询效率提升40%以上。
3.2 事务处理模式
保证数据一致性的关键:
using var transaction = await connection.BeginTransactionAsync();
try
{
// 扣减库存
var updateCmd = connection.CreateCommand();
updateCmd.CommandText = "UPDATE products SET stock = stock - 1 WHERE product_id = @id";
updateCmd.Parameters.AddWithValue("@id", 1001);
await updateCmd.ExecuteNonQueryAsync();
// 创建订单
var insertCmd = connection.CreateCommand();
insertCmd.CommandText = "INSERT INTO orders (...) VALUES (...)";
// 参数设置...
await insertCmd.ExecuteNonQueryAsync();
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}
事务处理中要注意命令对象必须关联到同一个事务实例。建议为每个命令单独创建对象以保证线程安全。
4. 关联技术深度解析
4.1 Dapper扩展应用
虽然MySqlConnector本身足够强大,但配合Dapper可以提升开发效率:
using Dapper;
var users = await connection.QueryAsync<User>("
SELECT user_id AS UserId, user_name AS UserName
FROM users
WHERE register_time > @startDate",
new { startDate = new DateTime(2023, 1, 1) });
public class User
{
public int UserId { get; set; }
public string UserName { get; set; }
}
Dapper的自动对象映射可以节省大量样板代码,同时保持高性能。注意属性名与列别名的对应关系。
5. 技术选型与应用场景
5.1 适用场景分析
- 实时交易系统:需要精确控制SQL执行的金融场景
- 大数据量报表:直接编写优化SQL进行复杂聚合运算
- 微服务架构:轻量级数据库访问层的最佳选择
- 遗留系统改造:平稳迁移原有ADO.NET代码的最佳方案
5.2 技术优势与局限
优势:
- 原生ADO.NET实现,无额外抽象层损耗
- 完善的连接池管理(默认最大100连接)
- 完全支持异步IO操作
- 丰富的参数类型支持(包括JSON类型)
局限:
- 需要手动处理对象关系映射
- 复杂查询需要手写SQL维护
- 缺乏LINQ等高级查询语法
6. 关键注意事项
- 连接池陷阱:默认最小连接数为0,高并发场景建议设置合理的MinPoolSize
- 超时配置:合理设置CommandTimeout(默认30秒)和ConnectionTimeout(默认15秒)
- 编码问题:确保连接字符串指定正确的Character Set(推荐utf8mb4)
- 类型映射:注意MySQL的UNSIGNED类型需要使用MySqlUnsigned类型处理
- 资源释放:即使使用using语句也要注意嵌套事务时的资源释放顺序
7. 最佳实践总结
经过多个项目的实践验证,我们总结出以下黄金准则:
- 始终使用参数化查询
- 异步方法统一使用ConfigureAwait(false)
- 长时间操作定期检查连接状态(connection.PingAsync)
- 敏感信息查询开启SSL加密
- 生产环境必须配置重试策略(使用Polly等库)