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. 关键注意事项

  1. 连接池陷阱:默认最小连接数为0,高并发场景建议设置合理的MinPoolSize
  2. 超时配置:合理设置CommandTimeout(默认30秒)和ConnectionTimeout(默认15秒)
  3. 编码问题:确保连接字符串指定正确的Character Set(推荐utf8mb4)
  4. 类型映射:注意MySQL的UNSIGNED类型需要使用MySqlUnsigned类型处理
  5. 资源释放:即使使用using语句也要注意嵌套事务时的资源释放顺序

7. 最佳实践总结

经过多个项目的实践验证,我们总结出以下黄金准则:

  • 始终使用参数化查询
  • 异步方法统一使用ConfigureAwait(false)
  • 长时间操作定期检查连接状态(connection.PingAsync)
  • 敏感信息查询开启SSL加密
  • 生产环境必须配置重试策略(使用Polly等库)