I'm trying to build an IN clause for array of Guids for MySql query. Guid columns are represented as binary(16) in the DB. According to docs and answers here I should be able to do something like
var arrayOfGuidsFromDb = ...;
await dbconn.ExecuteAsync<T>("UPDATE ...
SET ...
WHERE SomeGuidField IN @Ids",
new { Ids = arrayOfGuidsFromDb }
I'm also using this Guid converter
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);
}
The issue with MySql though is that it tries (by default) to optimize GUID layout in the DB by rearranging the timestamp part of the GUID value. I decided not to change this behavior, it is working fine for reads/writes and conditions like WHERE SomeGuidField = @SomeGuid but for the IN statement in the question it matches 0 results. I was able to write this hack instead
guids.Select(guid => $"uuid_to_bin('{RotateToMatchInDbGuid(guid).ToString()}')")
where I convert each guid to a string and then string.Join(','... them for the IN clause, the helper method:
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);
}
This obviously doesn't look and feel right. Am I doing something wrong or is there some setting missing that I should enable to make Dapper behavior consistent for both = and IN GUID conditions?
Full code:
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
GuidFormat=TimeSwapBinary16connection string option (mysqlconnector.net/connection-options). That way, you shouldn't need the customMySqlGuidTypeHandlerbut the MySqlConnector ADO.NET library will serialize them (efficiently) on the wire whenever you use aGuidas aMySqlCommandparameter value (either directly, or generated by Dapper).INclausesGuidobjects on the wire.IN(not rotated) or in=(rotated) clause if I'm not mistaken