Как сравнить два байтовых массива с большим или меньшим, чем значение оператора в C# или Linq?
У меня в массиве Code First Entity Framework для SQL TimeStamps есть байтовый массив, как показано ниже:
[Column(TypeName = "timestamp")]
[MaxLength(8)]
[Timestamp]
public byte[] TimeStamps { get; set; }
Вышеуказанное свойство соответствует типу данных SQL-сервера "timestamp" в C#.
В SQL-сервере я могу легко сравнить "отметку времени", как показано ниже...
SELECT * FROM tableName WHERE timestampsColumnName > 0x000000000017C1A2
То же самое я хочу достичь в C# или Linq Query. Здесь я написал мой запрос Linq, который не работает должным образом.
byte[] lastTimeStamp = someByteArrayValue;
lstCostCenter.Where(p => p.TimeStamps > lastTimeStamp);
Я также пытался с BitConverter
сравнить двухбайтовый массив, который тоже не работает...
lstCostCenter.Where(p => BitConverter.ToInt64(p.TimeStamps, 0) > BitConverter.ToInt64(lastTimeStamp, 0));
Как я могу сравнить байтовые массивы в C# или Linq Query.
Примечание. Я просто не хочу сравнивать два массива, как обычно, используя SequenceEqual или любые другие методы, которые просто сравнивают и возвращают true или false. Мне нужно сравнение в запросе Linq с оператором Greater than > или Less than, которое дает правильные данные, такие как запрос SQL Server.
2 ответа
Одним из способов является использование IStructuralComparable
, который Array
неявно реализует:
byte[] rv1 = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01 };
byte[] rv2 = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x05 };
var result = ((IStructuralComparable)rv1).CompareTo(rv2, Comparer<byte>.Default); // returns negative value, because rv1 < rv2
Если по какой-то причине вы хотите использовать BitConverter
, вы должны обратить массивы, потому что BitConverter
на большинстве архитектур является прямым порядком байтов (чтобы быть в безопасности - вы должны проверить BitConverter.IsLittleEndian
поле и обратный только если он возвращает истину). Обратите внимание, что это не очень эффективно, чтобы сделать это.
var i1 = BitConverter.ToUInt64(rv1.Reverse().ToArray(), 0);
var i2 = BitConverter.ToUInt64(rv2.Reverse().ToArray(), 0);
Теперь, если вы используете Entity Framework и вам нужно сравнить временные метки в запросе к базе данных, ситуация несколько иная, потому что Entity Framework будет проверять выражение вашего запроса в поисках шаблонов, которые он понимает. Не понимает IStructuralComparable
сравнения (и BitConverter
преобразования тоже, конечно), так что вы должны использовать хитрость. Объявить метод расширения для байтового массива с именем Compare
:
static class ArrayExtensions {
public static int Compare(this byte[] b1, byte[] b2) {
// you can as well just throw NotImplementedException here, EF will not call this method directly
if (b1 == null && b2 == null)
return 0;
else if (b1 == null)
return -1;
else if (b2 == null)
return 1;
return ((IStructuralComparable) b1).CompareTo(b2, Comparer<byte>.Default);
}
}
И используйте это в запросе EF LINQ:
var result = ctx.TestTables.Where(c => c.RowVersion.Compare(rv1) > 0).ToList();
При анализе EF увидит метод с именем Compare
и совместимая подпись, которая переведет ее в правильный запрос sql (выберите * в таблице, где RowVersion > @yourVersion)
Если вы знаете, что два байтовых массива имеют одинаковую длину и являются самыми старшими байтами, то это работает:
Func<byte[], byte[], bool> isGreater =
(xs, ys) =>
xs
.Zip(ys, (x, y) => new { x, y })
.Where(z => z.x != z.y)
.Take(1)
.Where(z => z.x > z.y)
.Any();
Если я проверю со следующим:
byte[] rv1 = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01 };
byte[] rv2 = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x05 };
Console.WriteLine(isGreater(rv1, rv2));
Console.WriteLine(isGreater(rv2, rv1));
... я получаю ожидаемый результат:
Ложь Правда