Как сравнить два байтовых массива с большим или меньшим, чем значение оператора в 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));

... я получаю ожидаемый результат:

Ложь
Правда
Другие вопросы по тегам