ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Visual Studio 2019连接MySQL:环境、连接与乱码排查

Visual Studio 2019连接MySQL:环境、连接与乱码排查 数据库课程设计、上位机项目、实验室管理系统只要业务数据落在 MySQL 上Visual Studio 2019 就是绝大多数人打开的第一个工具。但真正动手的时候麻烦往往不在代码本身装完 MySQL 找不到连接器、NuGet 里挑错版本、连接字符串少一个参数就报认证失败、中文存进去变成问号。这篇内容围绕 Visual Studio 2019 连接 MySQL 这条主线把环境前提、连接路线选型、第一条连接怎么跑通、乱码与时区的根因、报错排查链路、以及连接层怎么封装成可复用代码一层层拆开讲。适合正在做数据库课程设计的同学也适合从 Java、Python 转过来做 .NET 上位机、需要把 MySQL 接进去的开发者。1. 三个隐形前提决定了后面会不会白忙很多人对连接数据库的理解就是拽一个控件、填个服务器地址点测试连接。这个心智模型在 SQL Server 上勉强成立搬到 MySQL 上会反复撞墙。原因在于VS2019 连接 MySQL 这件事实际上是四方协同MySQL 服务端的版本、连接器Connector的版本、项目的目标框架、以及你在 VS 里用的是设计器还是纯代码。任何一方对不上报错信息都不会直白地告诉你原因。1.1 服务端版本决定了可用的认证方式MySQL 5.7 和 8.0 在认证机制上换过一次底。5.7 默认用mysql_native_password8.0 改成了caching_sha2_password。这个改动本身是安全上的进步但它让大量老版本的 .NET 连接器直接失效——Connector/NET 8.0.9 之前的版本根本不认识这个插件连接时会抛出Authentication method caching_sha2_password is not supported。所以第一件事是确认服务端版本再去挑连接器的版本。MySQL 服务端默认认证插件推荐 Connector/NET备注5.7.xmysql_native_password6.9.x / 8.0.x老项目兼容性最好8.0.9 之前无8.0.9 以下不支持 caching_sha28.0.11 ~ 8.0.32caching_sha2_password8.0.11 及以上需要 AllowPublicKeyRetrieval8.0.33 及以后caching_sha2_password8.0.33稳定性较好推荐提示如果你只是做课程设计、本机跑数据完全可以新建账号时显式指定IDENTIFIED WITH mysql_native_password BY 密码省掉一堆认证参数的麻烦。但生产环境不建议这么降级安全边界会变窄。1.2 could not find any instance of visual studio 到底卡在哪这个报错几乎是搜索量最高的 MySQL VS2019 组合问题它出现的场景非常固定你在跑 MySQL Installer 的时候勾选了 Products 里的MySQL for Visual Studio组件。这个组件的老版本只识别到 Visual Studio 2017而 VS2019 的内部版本号是 16.0。更麻烦的是VS2019 不再往注册表的HKLM\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\16.0下写安装信息改用 Setup Configuration API 查询老版 Installer 的探测逻辑自然找不到实例。两条修复路径都可行。第一条是把 MySQL Installer 升到 1.4.30 以上、MySQL for Visual Studio 升到 1.2.9 以上让它具备识别 VS2019 的能力。第二条更省事直接不装这个组件。MySQL for Visual Studio 提供的功能只有 Server Explorer 里的数据连接节点、以及部分设计期向导支持日常写代码根本用不到。缺失它不会导致MySql.Data引用不了用 NuGet 装包照常工作。1.3 目标框架决定了你能挑哪些包VS2019 新建项目时可以选择 .NET Framework 4.7.2也可以选择 .NET Core 3.1 或 .NET 5。这个选择会直接锁死你能用的数据访问库。做个对照就清楚了MySql.Data走 .NET Standard 2.0 路线两种框架都能用EF Core 相关的 MySQL Provider 里Pomelo.EntityFrameworkCore.MySql 从 5.0 开始面向 .NET Standard 2.1.NET Framework 项目根本加载不了。如果你选了 .NET Framework 又非要上 EF Core只能退回 Pomelo 3.x/2.x或者改用MySql.Data.EntityFramework走 EF6。我见过太多人卡在这里包装上了编译过了一运行抛FileLoadException排查半天发现是 Standard 版本不兼容。2. 数据访问路线怎么选ADO.NET、EF 还是 ODBC确定环境前提之后第二个决策是走哪条数据访问路线。三条路各有适用场景选错了不会报错但会在项目后期不断交学费。2.1 原生 ADO.NET课程设计和中小工具的首选原生 ADO.NET 就是MySqlConnectionMySqlCommandMySqlDataReader那一套。它的优势在于行为完全可预测SQL 是你手写的参数是你自己加的返回的DataTable结构清清楚楚。做课程设计、实验室管理、上位机数据采集这类场景表结构简单、查询不复杂ADO.NET 的代码量其实比 EF 还少而且调试的时候能一眼看出到底执行了什么 SQL。它唯一的缺点是要手写 SQL。但对初学者来说这恰恰是好事——数据库课程设计本来就需要你展示对 SQL 的掌握用 ORM 把 SQL 藏起来反而交不出东西。// 最基础的一条查询注意 using 的嵌套顺序 using (var conn new MySqlConnection(connStr)) { conn.Open(); using (var cmd new MySqlCommand(SELECT COUNT(*) FROM student, conn)) { long total Convert.ToInt64(cmd.ExecuteScalar()); Console.WriteLine($学生总数{total}); } }2.2 EF Core Pomelo模型先行的代价EF Core 适合业务实体多、关系复杂的项目用 Code First 从 C# 类生成表结构迁移Migration自动维护 schema 变更。Pomelo 是这个组合里社区维护得最好的 MySQL Provider异步支持、DbContext生命周期管理都很完善。代价有两个。一是版本敏感前面提到的 Standard 2.1 限制就是典型二是当 SQL 出问题时你需要多绕一层去理解 EF 生成的语句。所以我通常建议先用 ADO.NET 把表结构和业务数据跑通确认 MySQL 侧没问题再决定要不要上 EF。倒过来做一旦报错你分不清是 EF 配置错还是数据库错。2.3 ODBC 与 OleDb只在对接老系统时才考虑ODBC 路线的价值在于通用性。如果项目要同时对接 SQL Server、Oracle、达梦这类多种数据库用 ODBC 抽象一层可以少写很多分支代码。但在纯 MySQL 场景下ODBC 的性能和类型映射都比原生连接器差一截尤其是DATETIME、DECIMAL的处理容易出现精度或格式偏差。我的做法是除非有明确的异构数据库需求否则不碰 ODBC。2.4 MySql.Data 和 MySqlConnector 的许可证与异步差异这一条很多人不知道但商用项目必须搞清楚。MySql.Data是 Oracle 官方的 Connector/NET采用 GPL v2 加 FOSS 例外的双许可模式。这意味着如果你的项目不开源严格来说需要商业授权。MySqlConnector是社区独立实现的替代品采用 MIT 许可API 高度兼容而且在异步操作上做得比官方包更彻底——官方包早期版本的OpenAsync实际上是同步阻塞的伪异步。对比项MySql.Data官方MySqlConnector社区许可证GPL v2 FOSS 例外MIT命名空间MySql.Data.MySqlClientMySqlConnector异步支持部分方法伪异步完整真异步VS 设计器集成支持一般上手难度低教程多低API 基本一致做课程设计、内部工具用哪个都行。但如果是要交付给甲方的商业软件我一般直接用 MySqlConnector省得在许可证上扯皮。3. 从建库到界面显示把第一条连接跑通环境齐了、路线定了接下来就是动手。这一节按真实操作顺序走一遍每一步都说明为什么这么做。3.1 建库建表与最小权限账号先用 MySQL Workbench 或者命令行建库建表。字符集一定要在库级别就定成utf8mb4后面所有表继承它能省掉大量乱码排查。CREATE DATABASE IF NOT EXISTS course_design DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; USE course_design; CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, age TINYINT UNSIGNED, class_name VARCHAR(50), created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;为什么不用root账号连因为一旦你的连接字符串泄露或者程序被反编译整个数据库就全裸了。建一个只对course_design库有增删改查权限的账号是几乎零成本的防护。CREATE USER appuserlocalhost IDENTIFIED BY Str0ng#Pass2024; GRANT SELECT, INSERT, UPDATE, DELETE ON course_design.* TO appuserlocalhost; FLUSH PRIVILEGES;注意appuserlocalhost和appuser%是两个不同的账号。本机连用 localhost如果程序部署到局域网另一台机器要么改成%不推荐要么显式加上目标机器的 IP。3.2 连接字符串里每个参数在干什么这是整篇里最值得逐字看的部分。一个能稳定工作的连接字符串长这样Server127.0.0.1;Port3306;Databasecourse_design;Uidappuser;PwdStr0ng#Pass2024;CharSetutf8mb4;SslModeNone;AllowPublicKeyRetrievaltrue;Connection Timeout10;Poolingtrue;Min Pool Size2;Max Pool Size50;逐个解释Server127.0.0.1这里刻意不用localhost。Windows 10 之后localhost会优先解析成 IPv6 的::1而 MySQL 默认的bind-address只监听 IPv4。结果是解析成功、连接超时报错信息还特别含糊。用127.0.0.1可以绕开这个坑。CharSetutf8mb4告诉连接器用四字节 UTF-8 通信。只写utf8会导致部分表情符号和历史字符存不进去。SslModeNone本机开发关闭 SSL避免自签证书校验失败。生产环境必须开回来。AllowPublicKeyRetrievaltrue这是caching_sha2_password在非加密连接下的必要参数。首次认证时客户端需要向服务端索取 RSA 公钥来加密密码默认不允许所以必须显式开启否则报Retrieval of the RSA public key is not enabled for insecure connections。Pooling / Min Pool Size / Max Pool Size连接池开关和上下限。课程设计并发不高Min 设 2、Max 设 50 足够。Connection Timeout10建立连接的超时秒数。默认 15 秒遇到网络问题时界面会假死很久调到 10 秒体验更好。3.3 在 WinForms 里把数据读进 DataGridView建一个 Windows 窗体项目通过 NuGet 安装连接器。VS2019 里推荐用程序包管理器控制台指定版本避免自动选到有 breaking change 的最新版Install-Package MySql.Data -Version 8.0.33然后在窗体上加一个 TextBox 做关键字输入、一个 Button、一个 DataGridView写查询逻辑using System; using System.Data; using System.Configuration; using MySql.Data.MySqlClient; private void btnQuery_Click(object sender, EventArgs e) { string connStr ConfigurationManager .ConnectionStrings[CourseDb].ConnectionString; string sql SELECT id, name, age, class_name, created_at FROM student WHERE name LIKE kw ORDER BY id DESC LIMIT 200; using (var conn new MySqlConnection(connStr)) { try { conn.Open(); using (var cmd new MySqlCommand(sql, conn)) { cmd.Parameters.AddWithValue(kw, % txtKeyword.Text.Trim() %); var table new DataTable(); using (var reader cmd.ExecuteReader()) { table.Load(reader); } dataGridView1.DataSource table; } } catch (MySqlException ex) { MessageBox.Show($查询失败{ex.Number} - {ex.Message}); } } }DataTable.Load(reader)这个写法的好处是列名直接映射到 DataGridView 的表头省掉手工绑定的代码。LIMIT 200是随手加的保护课程设计的数据量不大但养成限制返回行数的习惯将来查生产库不会因为一条全表扫描把内存吃满。4. 连上之后才是麻烦中文乱码、时区与编码连接测试成功的那一刻很多人会松一口气然后把中文数据一存发现界面上是????。这类问题的根因永远在编码链路上而编码链路上有四个环节缺一环就出问题。4.1 utf8mb4 要贯穿服务端、连接、表结构和代码文件四个环节分别是MySQL 服务端/库/表的字符集、连接字符串的CharSet、C# 源文件的保存编码、以及输出层控制台或窗体控件的编码。服务端和表结构前面已经定了utf8mb4。连接字符串也带了CharSetutf8mb4。剩下两个容易被忽略第一C# 源文件本身的编码。VS2019 保存.cs文件的默认编码跟系统区域设置有关在某些中文 Windows 上会保存成 GB2312。如果你在代码里写字面量张三然后插入数据库这些字节在传输前就已经是错的。修复方式文件 → 高级保存选项 → 编码选UTF-8 带签名。或者干脆不在代码里写中文常量全部从配置或界面输入取值。第二控制台输出编码。写了Console.WriteLine却看到乱码在Main开头加一行Console.OutputEncoding System.Text.Encoding.UTF8;排查这类问题时用一条 SQL 直接验证服务端存的是什么比在 .NET 侧瞎猜快得多SELECT id, name, HEX(name), LENGTH(name), CHAR_LENGTH(name) FROM student LIMIT 5;HEX(name)打出来如果是E5BCA0E4B889说明存的是正确的 UTF-8 字节如果是D5C5C8FD那就是 GBK 的字节串存进去了问题出在客户端编码而不是服务端。4.2 时间字段偏移的根源与 SET time_zoneDATETIME和TIMESTAMP在 MySQL 里的行为不一样。DATETIME存的是字面值不做时区转换TIMESTAMP会按会话的time_zone做转换存的时候转成 UTC读的时候再转回会话时区。如果服务端的全局time_zone是SYSTEM或者00:00而你的服务器操作系统又是 UTC读出来的时间就会比预期少 8 小时。最直接的修法是在连接建立之后立刻执行一次会话级设置using (var conn new MySqlConnection(connStr)) { conn.Open(); using (var cmd new MySqlCommand(SET time_zone 08:00, conn)) { cmd.ExecuteNonQuery(); } // 后续查询在同一连接上进行时区设置生效 }更进一步的做法是在连接字符串里配置初始化语句让连接池每次新建物理连接时自动执行。也可以在服务端 my.ini 里把default-time-zone固定下来但改了要重启服务。我给的建议是需要跨时区展示的时间点用 TIMESTAMP需要精确表示某个本地时刻的比如课程表的第几节课用 DATETIME。搞混了类型后面无论怎么调参数都是打补丁。4.3 参数化查询不是可选项前面代码里用的是cmd.Parameters.AddWithValue而不是字符串拼接。原因有两层安全上防 SQL 注入性能上让 MySQL 可以复用执行计划。字符串拼接的写法WHERE name LIKE % keyword %只要用户输入一个单引号就能把语句结构撕开。参数化之后连接器会把参数值和 SQL 文本分开发送服务端不再把参数内容当语法解析。提示AddWithValue在类型推断上偶尔会出问题比如把int推成DECIMAL导致索引失效。更严谨的写法是cmd.Parameters.Add(kw, MySqlDbType.VarChar, 50).Value ...显式指定类型和长度。5. 报错逐条拆从认证失败到程序集冲突MySQL 的报错信息有两类一是服务端返回的错误码信息比较准确二是 .NET 侧的异常往往是配置或版本问题。这一节按排查难度从低到高排。5.1 认证插件相关报错的两条修复路径错误长这样MySqlException: Authentication method caching_sha2_password is not supported by any of the available plugins.修复路径一升级连接器。把MySql.Data升到 8.0.11 以上同时连接字符串加上AllowPublicKeyRetrievaltrue。这是推荐做法。修复路径二改账号的认证插件ALTER USER appuserlocalhost IDENTIFIED WITH mysql_native_password BY Str0ng#Pass2024; FLUSH PRIVILEGES;改完之后不用升级连接器也能连上。如果遇到的是Retrieval of the RSA public key is not enabled那就是少了AllowPublicKeyRetrievaltrue属于同一条线的另一个症状。5.2 无法连接到任何指定的 MySQL 主机 的排查顺序错误原文是Unable to connect to any of the specified MySQL hosts.这个报错的覆盖面很广可能的原因从网络到服务状态都有可能。按下面的顺序排查基本能在五分钟内定位服务真的在跑吗。Win R 输入services.msc找 MySQL 服务看状态是不是正在运行。Windows 上 MySQL 装完之后默认是自动启动的但如果之前手动停过重新开机不一定起来。端口通不通。命令行执行netstat -ano | findstr 3306看是否有 LISTENING。没有输出说明服务没监听。是不是 IPv6 的问题。把Serverlocalhost换成Server127.0.0.1再试。这一步能解决相当比例的配置明明没错但就是连不上。防火墙。本机连接一般不受影响但如果 MySQL 装在虚拟机或另一台机器上3306 端口要放通。账号的 host 匹配。前面提过appuserlocalhost和appuser127.0.0.1在 MySQL 的权限体系里是两条记录。用SELECT user, host FROM mysql.user;确认一下。5.3 目标框架与程序集版本冲突这一类的表现是编译能过一运行就抛Could not load file or assembly MySql.Data, Version8.0.33.0, Cultureneutral, PublicKeyTokenc5687fc88969c44d or one of its dependencies.或者反过来报的是版本号对不上。根因通常是项目里 NuGet 引用的是 8.0.33但全局程序集缓存GAC里注册了一个旧版MySql.Data.dll运行时按 GAC 优先的策略加载了旧版。GAC 里的那个 DLL 大概率是装 MySQL for Visual Studio 组件时带进去的。三步处理第一步确认 NuGet 包版本。在管理 NuGet 程序包 → 已安装里看清楚实际版本。第二步查 GAC。用管理员权限打开C:\Windows\assembly或者运行gacutil /l MySql.Data看有没有注册。有的话把 MySQL for Visual Studio 卸载掉或者手动从 GAC 移除。第三步如果确实需要在多个版本间共存加程序集重定向configuration runtime assemblyBinding xmlnsurn:schemas-microsoft-com:asm.v1 dependentAssembly assemblyIdentity nameMySql.Data publicKeyTokenc5687fc88969c44d cultureneutral / bindingRedirect oldVersion0.0.0.0-8.0.33.0 newVersion8.0.33.0 / /dependentAssembly /assemblyBinding /runtime /configuration还有一个隐蔽的坑是平台目标。老版本的 Connector 里带有非托管组件需要 32 位和 64 位分别对应。如果项目是 Any CPU 且勾了首选 32 位而系统里的 DLL 只有 64 位版本就会抛BadImageFormatException。8.0 之后的版本基本是纯托管实现这个问题少了很多但如果你用的是很老的教程里推荐的 6.x 版本就要留意。6. 把连接层封起来配置、连接池与事务单次查询能跑通只是起点。真正写项目的时候连接字符串不能硬编码在代码里连接对象不能到处 new多条写操作必须能回滚。6.1 连接字符串放配置文件硬编码的连接字符串会在两个场景里坑你一是改数据库地址要重新编译二是源码一泄露账号密码全暴露。在项目里添加App.config写进连接字符串节?xml version1.0 encodingutf-8 ? configuration connectionStrings add nameCourseDb connectionStringServer127.0.0.1;Port3306;Databasecourse_design;Uidappuser;PwdStr0ng#Pass2024;CharSetutf8mb4;SslModeNone;AllowPublicKeyRetrievaltrue;Poolingtrue;Min Pool Size2;Max Pool Size50; providerNameMySql.Data.MySqlClient / /connectionStrings /configuration读取方式string connStr ConfigurationManager.ConnectionStrings[CourseDb].ConnectionString;需要引用System.Configuration程序集。.NET Core 项目里没有App.config这套机制改用它自己的appsettings.json加配置绑定。注意App.config里的密码是明文的只是比写在代码里好一点。要再上一层可以用 DPAPI 加密这个配置节普通工具类项目一般不至于。6.2 连接池与 using 的正确理解using (var conn new MySqlConnection(...))里的Dispose并不是真的关闭 TCP 连接而是把连接归还到池里。这是一个关键认知连接池按连接字符串做键只要字符串完全一致包括大小写和参数顺序差异也会被认为不同就能复用。所以有两条实践建议。第一不要在运行时拼接连接字符串比如加个ApplicationNamexxx来区分用户那样每个变体都会各自建一个池。第二不要手动调conn.Close()又忘了Dispose用using包住最省心。连接池满了会怎样表现为程序卡住等到Connection Timeout之后抛异常。排查方式是看SHOW PROCESSLIST里的连接数或者用MySqlConnection.ClearAllPools()强制清池。日常开发里池满八成是因为某处ExecuteReader之后没关闭 Reader占着连接不放。// 正确的嵌套Reader 在 Command 里Command 在 Connection 里 using (var conn new MySqlConnection(connStr)) using (var cmd new MySqlCommand(sql, conn)) { conn.Open(); using (var reader cmd.ExecuteReader()) { while (reader.Read()) { // 处理行数据 } } }6.3 事务与批量写入涉及多条写操作的业务比如添加学生并同时写一条选课记录必须用事务保证原子性。MySQL 的 InnoDB 引擎支持事务前提是表的引擎是 InnoDB 而不是 MyISAM。using (var conn new MySqlConnection(connStr)) { conn.Open(); using (var tran conn.BeginTransaction()) { try { using (var cmd new MySqlCommand(INSERT INTO student(name, age) VALUES(n, a), conn, tran)) { cmd.Parameters.Add(n, MySqlDbType.VarChar, 50).Value 李四; cmd.Parameters.Add(a, MySqlDbType.Int32).Value 20; cmd.ExecuteNonQuery(); } // 其余写操作全部传同一个 tran tran.Commit(); } catch { tran.Rollback(); throw; } } }如果要一次性插入几千条数据逐条ExecuteNonQuery会慢到无法接受。两条路一是拼接多值 INSERTINSERT INTO t(a,b) VALUES(...),(...),(...);注意别超过max_allowed_packet默认 4MB 或 64MB二是用MySqlBulkLoader从 CSV 批量导入速度最快但要求数据源是文件。7. 做数据库课程设计时踩出来的几个真实教训第一次用 VS2019 连 MySQL 那会儿我在认证插件上耗掉了整个下午。当时装的是 MySQL 8.0跟着一篇 5.7 时代的教程走连接字符串里连CharSet都没有结果就是连不上。后来才明白教程的时效性在这个组合上体现得特别明显——MySQL 的认证机制、连接器的版本、VS 的注册表结构任意一环变了老教程就会把人带沟里。再后来的经验是先确认服务端版本再倒推连接器版本别反过来。看到caching_sha2_password相关报错第一反应不是去改数据库配置而是看 NuGet 里装的是哪个版本。还有一个反复出现的场景是中文乱码。我的固定排查动作是先跑SELECT HEX(字段)用HEX的结果反推是服务端存错了还是客户端显示错了。E5BCA0对应的就是张字正确的 UTF-8 编码看到D5C5就知道是 GBK 混进来了。这一步能直接砍掉一半的猜测时间。最后提一个容易被忽视的小细节项目里如果同时引用了MySql.Data和某个第三方库比如某些 ORM、报表工具要注意它们各自依赖的连接器版本。出现过两次运行时报找不到指定版本程序集最后都是靠bindingRedirect统一版本解决的。加那条重定向之前先去看输出目录bin\Debug里实际落地的 DLL 版本号比在项目引用里翻要快。
返回列表