Windows 7 下 MySql Guid 问题
MySQL之前的文章提到过关于 MySql 数据库要如何保存 .NET Guid 类型的问题.
根据 MySQL Connector Net Version 5.2 - 2/11/2008 CHANGES 中提到的:
BINARY(16) columns are now returned as Guid objects
那么使用 BINARY(16) 列做为 Guid 的载体是理所当然的了.
通过一系统的试验, 读取是没问题的, 写入时要先转化为二进制数组 (ToByteArray), 因为只有这样才能保证长度是16, 否则任何方法都会告诉你数据太长.
我是个不安分的人, 正如你在标题中看到的, 刚刚忍不住装上了 Windows 7 beta1, 在一阵兴奋和陶醉过后, 撞上了第一个棘手的问题:
#!fsharp
#light
#r @"D:\Program Files\MySQL\MySQL Connector Net 5.2.5\Binaries\.NET 2.0\MySql.Data.dll"
open System
open System.Data
open MySql.Data.MySqlClient
let conn =
let builder = MySqlConnectionStringBuilder(CharacterSet = "utf8", Server = "localhost", UserID = "root", Password = "erp", Database = "erp")
let conn = new MySqlConnection(builder.ConnectionString)
conn.Open()
conn
let cmd = conn.CreateCommand()
cmd.CommandText <- "
DROP TABLE IF EXISTS `TestGuid`;
CREATE TABLE IF NOT EXISTS `TestGuid`
(`ID` BINARY(16))
ENGINE = InnoDB;
INSERT
INTO `TestGuid`
VALUES (0x012345670123012301230123456789ab);
INSERT
INTO `TestGuid`
VALUES (?_ID);"
let guid = Guid("12345678-1234-1234-1234-123456789abc")
cmd.Parameters.AddWithValue("_ID", guid.ToByteArray())
cmd.ExecuteNonQuery()
cmd.CommandText <- "
SELECT *
FROM `TestGuid`"
let reader = cmd.ExecuteReader()
while reader.Read() do
for i in 0 .. reader.FieldCount - 1 do
print_any (reader.GetValue i)
printfn ""
Console.ReadKey()
上面这段代码只是简单的创建一个名为 TestGuid 的表, 只有的一个名为 ID 的 BINARY(16) 列.
第一行数据直接插入 0x012345670123012301230123456789ab (从0开始, 按 8 4 4 4 12 分5部分)
第二行数据使用参数 Guid(“12345678-1234-1234-1234-123456789abc”) (从1开始, 规律同上)
之后又将数据取回, 输出到命令行, 猜猜看在 Windows 7 中结果是什么?
67452301-2301-2301-0123-0123456789ab
12345678-1234-1234-1234-123456789abc
注意看第一行, 前半部分, 也就是 8 4 4 的部分, 顺序乱了…
Guid 去掉中间的连字符 “-“, 共32个字符, 每两位合并后可以获得16个字节, 那么第一条数据的前8个字节顺序被搞反了, 而且是在各自的组内被搞反的.
让我们再回到数据库中看一眼
#!sql
SELECT HEX(ID) FROM TestGuid
HEX(ID)
'012345670123012301230123456789AB'
'78563412341234121234123456789ABC'
天~ 这次是第二个反了.
冷静的想一想, 看来 MySql Server 是没有问题的, 问题出在 MySQL Connector Net 上面. 它在写入数据的时候搞错了二进制的高位与低位, 在读取的时候同样又搞错了一次, 错上加错, 结果似乎是对了, 只不过这个问题在其他系统中是不存在的, 换句话说, 这个问题带来的是跨系统之间数据不兼容.
没办法, 只能判断一下数据类型和操作系统版本, 暂时的规避一下吧:
#!fsharp
// 先给 List 扩展一个切片方法, 以便支持 lst.[1 .. 3] 这样的操作
type Microsoft.FSharp.Collections.List<'a> with
member t.GetSlice(n1, n2) =
let l = t.Length - 1
let n1 = match n1 with Some x -> max x 0 | _ -> 0
let n2 = match n2 with Some x -> min x l | _ -> l
[ for i in n1 .. n2 -> List.nth t i ]
/// 修正 MySql 不能识别 Windows7 Guid 的问题
let repairWin7Guid (x :obj) =
match x with
| :? Guid as g ->
if Environment.OSVersion.Version.Major = 6 && Environment.OSVersion.Version.Minor >= 1 then
let w = g.ToByteArray() |> Array.to_list
let r = w.[3] :: w.[2] :: w.[1] :: w.[0] :: w.[5] :: w.[4] :: w.[7] :: w.[6] :: w.[8 ..] |> Array.of_list
Guid(r) |> box
else
x
| _ -> x
这样做似乎不能彻底的解决问题, 幸好我的项目中 Guid 的出入口只有一个, 只要加在这两处就行了.
越来越对 MySql 不放心了 T_T