如何在C#或Linq中比较大于或小于运算符值的两个字节数组?

时间:2017-02-28 07:28:07

标签: c# arrays sql-server linq timestamp

我在Code TimeStamps的Code First Entity Framework中有一个字节数组,映射如下:

 [Column(TypeName = "timestamp")]
 [MaxLength(8)]
 [Timestamp]
 public byte[] TimeStamps { get; set; }

上述属性等于C#中的SQL服务器“timestamp”数据类型。

在SQL server中,我可以轻松比较“timestamp”,如下所示......

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查询中的比较大于>或小于<运算符,提供SQL Server查询等适当的数据。

2 个答案:

答案 0 :(得分:10)

一种方法是使用IStructuralComparableArray隐式实现:

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字段和只有在返回true时反转)。请注意,这样做效率不高。

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&gt; @yourVersion)

答案 1 :(得分:0)

如果您知道两个字节数组的长度相等且是最重要的字节,则可以使用:

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));

...我得到了预期的结果:

False
True