【发布时间】:2020-05-26 14:19:27
【问题描述】:
我正在尝试为 MySql 查询的 Guids 数组构建一个 IN 子句。 Guid 列在 DB 中表示为 binary(16)。根据这里的文档和答案,我应该能够做类似
var arrayOfGuidsFromDb = ...;
await dbconn.ExecuteAsync<T>("UPDATE ...
SET ...
WHERE SomeGuidField IN @Ids",
new { Ids = arrayOfGuidsFromDb }
我也在用这个 Guid 转换器
class MySqlGuidTypeHandler : SqlMapper.TypeHandler<Guid>
{
public override void SetValue(IDbDataParameter parameter, Guid guid) => parameter.Value = guid.ToByteArray();
public override Guid Parse(object value) => new Guid((byte[])value);
}
不过,MySql 的问题是它尝试(默认情况下)通过重新排列 GUID 值的时间戳部分来优化 DB 中的 GUID 布局。我决定不改变这种行为,它适用于读/写和WHERE SomeGuidField = @SomeGuid 等条件,但对于问题中的IN 语句,它匹配 0 个结果。我可以改写这个 hack
guids.Select(guid => $"uuid_to_bin('{RotateToMatchInDbGuid(guid).ToString()}')")
我将每个 guid 转换为字符串,然后将 string.Join(','... 转换为 IN 子句,即辅助方法:
static Guid RotateToMatchInDbGuid(Guid source)
{
Span<byte> result = stackalloc byte[16];
source.TryWriteBytes(result);
Swap(result, 0, 3);
Swap(result, 1, 2);
Swap(result, 4, 5);
Swap(result, 6, 7);
return new Guid(result);
}
这显然看起来和感觉都不对。我是做错了什么,还是缺少一些设置,我应该启用以使 Dapper 行为在 = 和 IN GUID 条件下保持一致?
完整代码:
Guid[] guids = await dbConn.QueryAsync("SELECT Id FROM SomeTable"); //returns 1 row
// query using IN clause and array param:
var usingIn = await dbConn.QueryAsync("SELECT * From SomeTable WHERE Id IN @Ids", new { Ids = guids}); // returns 0 rows, should be 1
// now query using the `=` operator and same param but as a single value
var usingEquals = await dbConn.QueryAsync("SELECT * From SomeTable WHERE Id = @Id", new { Id = guids.First() }); // returns 1 row as expected
// query using array as CSV and no params
var usingCSV = await dbConn.QueryAsync($"SELECT * From SomeTable WHERE Id IN ({BuildCsv(guids)})"); // also returns 1 row as expected
【问题讨论】:
-
我建议切换到nuget.org/packages/MySqlConnector 并使用其
GuidFormat=TimeSwapBinary16连接字符串选项(mysqlconnector.net/connection-options)。这样,您不需要自定义MySqlGuidTypeHandler,但只要您使用Guid作为MySqlCommand参数值(直接或由小巧玲珑)。 -
谢谢,但我已经有很多使用 Dapper 编写的代码,这很好,我唯一遇到的问题是当我开始添加
IN子句时的这个问题 -
Dapper 与 MySqlConnector 配合得很好;无需更改任何现有代码。只需换掉 MySQL ADO.NET 库,让它处理网络上
Guid对象的序列化。 -
注意:guid 作为字节在数据库中非常尴尬,因为有多个互斥布局;没有人同意它应该是什么
-
@MarcGravell 我明白了,我担心的是,如果我在
IN(未旋转)或=(旋转)子句中使用相同的 guid 参数,Dapper 会以不同方式处理它我没记错