專案因資料量暴增,決定將後端從 Access 遷移至 MySQL。理想很豐滿,現實卻是「問題叢生」:中文亂碼、日期格式衝突、文字截斷、Blob 存儲失敗、甚至官方驅動拋出 ArgumentOutOfRangeException。本文還原遷移全過程踩坑與解決方案,為同路人趟雷。
default-character-set=utf8(或 utf8mb4)Charset=utf8(5.0+ 免 useUnicode=true)CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci花費數小時調通,核心是「全鏈路編碼一致」。
ToString() 含「上午/下午」Access 接受 2024/1/15 上午 10:30:00,MySQL 直接報語法錯誤。
修正:持久層統一格式化為 yyyy-MM-dd HH:mm:ss(24 小時制),或改用參數化查詢傳 DateTime 物件(推薦)。
現象:英文正常,中文截斷。懷疑編碼導致位元組長度超限。
嘗試:重建資料庫、調整欄位型別 → 無效。
終極方案:DROP DATABASE 重建全庫(含 charset/collate),問題消失。推測為建庫時編碼不一致導致儲存邏輯異常。
byte[] 直接寫入 → 成功FileStream / 檔案路徑 → 失敗變通:持久層統一將 Blob Base64 編碼 為字串存入 LONGTEXT,讀取時解碼。犧牲 33% 空間換取穩定性。
ArgumentOutOfRangeException:堆疊直指 MySQLString.WriteValueSystem.ArgumentOutOfRangeException: 索引和長度必須引用該字符串內的位置。
參數名: length
在 System.String.InternalSubStringWithChecks(...)
在 MySQL.Data.Types.MySQLString.WriteValue(...)
在 MySQL.Data.MySQLClient.MySQLParameter.Serialize(...)
在 MySQL.Data.MySQLClient.Statement.BindParameters()
在 MySQL.Data.MySQLClient.PreparableStatement.Execute()
在 MySQL.Data.MySQLClient.MySQLCommand.ExecuteNonQuery()
現場:表 POS_SALESORDER,參數化插入。無法在 VS 中斷點調試(驅動內部拋出)。
推測:某字串參數長度超過欄位定義,或編碼轉換時位元組長度計算邏輯 Bug(MySql.Data 6.9.x 常見)。
規避:升級驅動至 8.0.x,或改用 MySqlConnector (NuGet) 替代官方驅動,後者開源、效能更好、Bug 更少。
遷移 MySQL 本身是正確選擇,但工具鏈成熟度差距讓 .NET 開發者極度不適應。
| 問題 | 核心原因 | 解決方案 |
|---|---|---|
| 中文亂碼 | 編碼鏈路不一致 | my.ini + 連接字串 + 建庫語句 三級統一 utf8mb4 |
| 日期格式錯誤 | C# 預設含中文上午/下午 | 參數化傳 DateTime 物件 / 統一格式化 yyyy-MM-dd HH:mm:ss |
| 文字截斷 | 建庫編碼不一致 | 重建庫指定 charset/collate |
| Blob 存檔失敗 | 驅動處理 Stream 邏輯缺陷 | Base64 編碼存 LONGTEXT / 升級驅動 |
| 驅動 ArgumentOutOfRange | 字串長度/編碼轉換 Bug | 升級 MySql.Data 8.0+ 或遷移 MySqlConnector |
技術選型不僅看「功能支援」,更要評估「工具鏈成熟度、社區活躍度、遷移成本」。.NET 團隊引入 MySQL 時,建議:
mysql 命令列工具,而非依賴 Java 系遷移工具遷移是痛苦的,但完成後擁有的高效能、開源免費、跨平台優勢,值得這場「渡劫」。