你有三个主要问题。它不应该这么复杂,但确实如此。
这是我使用的解决方案:Timestamp.cs。这要容易得多。我会在最后举一个例子。
1. 不将苹果与苹果进行比较
rowVersion
是一个 8 字节数组。每个字节代表一个64 位整数的一部分,范围为 0 - 255。
System.Text.ASCIIEncoding.ASCII.GetBytes
编码 ASCII 字符串,而不是整数。它返回一个 18 字节的数组。每个字节代表一个文本字符,将是 '0' (48) - '9' (57)、'A' (65) - 'F' (70) 或 'x' (120)。
解决方案:你在正确的轨道上long.Parse("0x0000000000038B8C".Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
2. SQL Server 时间戳是大端存储的
BitConverter.ToUInt64
是 big-endian 还是 little-endian,这取决于您运行的系统ulong
是 big-endian 还是 little-endian。你可以自己看看这个。无论您在哪个系统上运行,您都需要一个始终为大端的转换:
static ulong BigEndianToUInt64(byte[] bigEndianBinary)
{
return ((ulong)bigEndianBinary[0] << 56) |
((ulong)bigEndianBinary[1] << 48) |
((ulong)bigEndianBinary[2] << 40) |
((ulong)bigEndianBinary[3] << 32) |
((ulong)bigEndianBinary[4] << 24) |
((ulong)bigEndianBinary[5] << 16) |
((ulong)bigEndianBinary[6] << 8) |
bigEndianBinary[7];
}
3. 二进制比较是无符号的
与 SQL Server 相比0x0FFFFFFFFFFFFFFF < 0xFFFFFFFFFFFFFFFF
,0xFFFFFFFFFFFFFFFF
更大。为了保持 SQL Server 处理它的相同含义,您必须使用ulong
and notlong
。否则0xFFFFFFFFFFFFFFFF
变成-1L
而不是 SQL Server 认为的那样,ulong.MaxValue
.
诚然,在使用列的高位之前必须发生 9 万亿次事情timestamp
,但是您可以使用相同的代码来比较以binary(8)
其他方式生成的两个时间戳。重要的是复制 SQL Server 的比较行为。
最干净的解决方案
这是我使用的解决方案:Timestamp.cs。
您的代码变为:
var existingRowVersion = (Timestamp)ulong.Parse(rowVersion.Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
var result = new MyContext().employees.ToList().Where(x => x.Id == 2 && (Timestamp)x.RowVersion > exisitingRowVersion);
基本上,一旦你投到Timestamp
,你就不会出错。
可悲的是,无论您使用哪种方法,都没有在服务器端而不是客户端应用此过滤器的好方法。这就是这个问题的主题。猜猜我发现了什么!使用 Entity Framework 6.1.3的一种方法!多么酷啊?
但是,这与您的问题无关,您绝对应该将Id == 2
过滤器放在服务器端(在调用 ToList 之前)。否则,您会将整个表传输到您的应用程序,然后在客户端丢弃除一行之外的所有内容。你应该做这个:
var existingRowVersion = (Timestamp)ulong.Parse(rowVersion.Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
var result = new MyContext().employees.Where(x => x.Id == 2).ToList().Where((Timestamp)x.RowVersion > exisitingRowVersion);
最好的:
var existingRowVersion = (Timestamp)ulong.Parse(rowVersion.Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
var employee = new MyContext().employees.SingleOrDefault(x => x.Id == 2);
if (employee == null) ... // Deleted
else if ((Timestamp)employee.RowVersion > exisitingRowVersion) ... // Updated