必须用 mysqlconnector:执行 dotnet add package mysqlconnector,禁用 mysql.data;连接字符串加 allow user variables=true 和 use timezone=true;用 using/await using 管理资源;参数用 mysqlparameter 显式声明;空值判 null 而非 dbnull.value。

必须用 MySqlConnector,别碰 MySql.Data(Oracle 官方驱动)——它在 .NET 6+ 上有连接泄漏、异步挂死、时区错乱等真实线上问题。
怎么装对的驱动包
只执行这一条命令:
dotnet add package MySqlConnector
不是 MySql.Data,不是 MySql.Data.EntityFrameworkCore,也不是 NuGet 搜索排第一的那个“MySQL Connector/NET”。MySqlConnector 是纯 C# 实现、MIT 协议、持续维护、.NET Core / .NET 5+ 原生友好的替代品。
- 若项目已引用
MySql.Data,先卸载它,再装MySqlConnector,否则可能因类型冲突报TypeLoadException -
MySqlConnector默认启用Allow User Variables=true,兼容老 SQL 写法;MySql.Data默认关闭,容易导致@var参数解析失败 - 它支持
MySqlDbType.Time和TimeSpan直接映射,而MySql.Data会把TIME字段转成DateTime导致值偏移
怎么写安全的连接与查询
用 MySqlConnection + MySqlCommand,但必须配合 using 或 await using 确保释放:
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
string connStr = "Server=localhost;Port=3306;Database=test;Uid=root;Pwd=123456;Allow User Variables=true;";
await using var conn = new MySqlConnection(connStr);
await conn.OpenAsync();
<p>await using var cmd = new MySqlCommand("SELECT id, name FROM users WHERE status = @status", conn);
cmd.Parameters.Add(new MySqlParameter("@status", 1));</p><p>await using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
int id = reader.GetInt32("id");
string name = reader.GetString("name");
// ...
}</p>
- 连接字符串里加
Allow User Variables=true,否则带@var的语句(比如存储过程调用)会直接报错Unknown column '@var' in 'field list' - 参数必须用
@name形式,且Add()传入MySqlParameter实例,不要用AddWithValue()—— 它会把null推断为INT,导致WHERE col = @p索引失效 -
ExecuteReaderAsync()返回的MySqlDataReader不支持多次遍历,读完即关,别试图reader.ToList()
怎么处理 DateTime 时区和精度问题
MySQL 的 DATETIME 没有时区,TIMESTAMP 有,但 .NET DateTime 默认是 Unspecified。不显式配置,就会出现“存进去是 10:00,查出来变 18:00”。
- 连接字符串加
Use Timezone=true;Default Command Timeout=30;,并确保 MySQL 服务端时区设为+08:00(或你的本地时区) - 读取
DATETIME字段时,用reader.GetDateTime("col");如果字段实际存的是 UTC 时间,手动调用.ToUniversalTime() - 写入前,统一转成
DateTimeKind.Utc或DateTimeKind.Local,避免依赖系统默认行为 - 高精度时间(毫秒级)要确认 MySQL 列定义是
DATETIME(3),且连接字符串含Allow User Variables=true,否则小数位会被截断
为什么 ExecuteScalarAsync 返回 null 而不是 DBNull.Value
这是 MySqlConnector 的明确行为:当查询结果为空(如 SELECT COUNT(*) FROM x WHERE 0=1),ExecuteScalarAsync() 返回 null,不是 DBNull.Value。
- 别写
if (result == DBNull.Value),应该写if (result is null) - 数值类型转换要加空值检查:
int count = result as int? ?? 0; - 这个设计是为了和 .NET 的异步惯用法一致,但和旧驱动不兼容——迁移时这里最容易漏改,导致
NullReferenceException
真正麻烦的从来不是连上数据库,而是连接没关、时区漂移、参数推断错类型、空值判断写反——这些点踩中一个,就足够让凌晨三点的告警响起来。










