Я пытаюсь создать предложение IN
для массива Guid
s для запроса MySql. Столбцы Guid
представлены в БД как 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 в базе данных, переставляя часть отметки времени значения GUID. Я решил не изменять это поведение, оно отлично работает для операций чтения / записи и таких условий, как WHERE SomeGuidField = @SomeGuid
, но для оператора IN
в вопросе оно соответствует 0 результатам. Вместо этого мне удалось написать этот хак
guids.Select(guid => $"uuid_to_bin('{RotateToMatchInDbGuid(guid).ToString()}')")
, в котором я конвертирую каждый идентификатор в строку, а затем 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