可遇不可求的Question之MySqlClient的Guid 類型的映射篇

關於 Guid 類型的映射

MySql 沒有原生的 Guid 類型,通常使用 binary(16) 或者 char(36) 這兩個類型。早期版本的 Connector/Net 將 binary(16) 自動映射成爲 Guid 類型。從 Connector/Net 6.1.1 開始,binary(16) 再也不被自動映射成爲 Guid 類型,而選用 char(36)。經過鏈接字符串的 Old Guids = true 選項可以使用舊的 binary(16) 映射,這個選項的缺省值是 false,即便用 char(36)。參考 21.2.6. Connector/NET Connection String Options Referencehtml

string connectionString = "server=localhost;user id=user;password=pwd;database=db;character set=utf8;Old Guids=true";mysql

遺憾的是,在最新的發佈版本 Connector/Net 6.2.3 中,缺乏像 Treat Tiny As Boolean=false 這樣的開關,沒法關閉從 char(36) 到 Old Guid 的自動映射。所以,當你的數據庫中恰巧使用了 char(36) 這個類型且這個字段存了不是合法 Guid 格式的字符值(中間用短橫線隔開的 32 個數字 ,即 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx),那麼你的程序就會報錯。空字符是一個常見的狀況,咱們最近的一個系統就是被這個害慘了。雖然,咱們明確這個 char(36) 類型字段是存應用程序生成的 Guid,並且加了 NOT NULL 約束,但在其餘應用程序(PHP實現)中因爲種種不可修復的緣由,偶爾會向這個字段入了空字符,致使現有應用程序(.NET實現)讀取失敗。更要命的是,這個錯誤是在 MySqlDataReader.Read() 方法發生的,異常堆棧以下:git

System.FormatException: Guid should contain 32 digits with 4 dashes (xxxxxxxx-xx
xx-xxxx-xxxx-xxxxxxxxxxxx).
   at System.Guid..ctor(String g)
   at MySql.Data.Types.MySqlGuid.MySql.Data.Types.IMySqlValue.ReadValue(MySqlPac
ket packet, Int64 length, Boolean nullVal) in E:\MySQL\mysql-connector-net-6.2.3
-src\MySql.Data\Provider\Source\Types\MySqlGuid.cs:line 234
   at MySql.Data.MySqlClient.NativeDriver.ReadColumnValue(Int32 index, MySqlFiel
d field, IMySqlValue valObject) in E:\MySQL\mysql-connector-net-6.2.3-src\MySql.
Data\Provider\Source\NativeDriver.cs:line 648
   at MySql.Data.MySqlClient.Driver.ReadColumnValue(Int32 index, MySqlField fiel
d, IMySqlValue value) in E:\MySQL\mysql-connector-net-6.2.3-src\MySql.Data\Provi
der\Source\Driver.cs:line 415
   at MySql.Data.MySqlClient.ResultSet.ReadColumnData(Boolean outputParms) in E:
\MySQL\mysql-connector-net-6.2.3-src\MySql.Data\Provider\Source\ResultSet.cs:lin
e 272
   at MySql.Data.MySqlClient.ResultSet.NextRow(CommandBehavior behavior) in E:\M
ySQL\mysql-connector-net-6.2.3-src\MySql.Data\Provider\Source\ResultSet.cs:line
210
   at MySql.Data.MySqlClient.MySqlDataReader.Read() in E:\MySQL\mysql-connector-
net-6.2.3-src\MySql.Data\Provider\Source\datareader.cs:line 875
   at LeoLab.MySql.GuidChar36Test.Program.Main(String[] args) in E:\LeoLab.MySql
\LeoLab.MySql.GuidChar36Test\LeoLab.MySql.GuidChar36Test\Program.cs:line 30 sql

這個錯誤信息中沒有包含是哪一列哪一行發生了錯誤,所以,當你的 SELECT 語言包含不少字段,結果集又比較大的時候,就暈菜了,不知道這個 char(36) 與 Guid 映射關係的就被搞得莫名其妙的了。數據庫

目前最快速的解決方法,是「曲線救國」:經過設置 Old Guids=true 啓用 binary(16) 到 guid 的映射來間接禁用 char(36) 到 Guid 的映射。ide

此外,我修改了 Connector/Net 6.2.3 的源碼,增長了一個相似 Treat Tiny As Boolean 的 Treat Char As Guid 選項類禁用 char(36) 到 Guid 的映射。大概修改以下:ui

1. MySqlConnectionStringBuilder 中增長屬性 TreatCharAsGuidspa

 

01. [Category("Advanced")] 
02. [DisplayName("Treat Char As Guid")] 
03. [Description("Should the provider treat CHAR(36) columns as Guid.")] 
04. [DefaultValue(true)] 
05. [ValidKeywords("Treat Char As Guid")] 
06. [RefreshProperties(RefreshProperties.All)] 
07. public bool TreatCharAsGuid 
08.
09.     get { return (bool)values["Treat Char As Guid"]; } 
10.     set { SetValue("Treat Char As Guid", value); } 
11. }

 

2. MySqlField 類的 SetTypeAndFlags 方法中的判斷 if (Type == MySqlDbType.String && CharacterLength == 36 && !driver.Settings.OldGuids) 中增長TreatCharAsGuid的判斷.net

 

1. if (driver.Settings.TreatCharAsGuid && Type == MySqlDbType.String && CharacterLength == 36 && !driver.Settings.OldGuids) 
2.             
3.                 mySqlDbType = MySqlDbType.Guid; 
4.             }

 

這樣咱們在鏈接字符串中指定 Treat Char As Guid=false,char(36) 就不會被映射成爲 Guid 了,而是普通 String 。code

源碼下載 MySqlConnectorNet6.2.3-TreatCharAsGuid.zip (308.80 kb)

相關文章
相關標籤/搜索