3

rowVersion从数据库中获取byte[8]

var rowVersion= new MyContext().Employee.FirstOrDefault(x => x.Id).rowVersion;
// suppose above the actual databse values is 0x0000000000038B8C
var rowVersionToLong = BitConverter.ToInt64(rowVersion,0);

现在,如果我手动执行此操作:

String rowversionStr = "0x0000000000038B8C";
byte[] mybyteArray = System.Text.ASCIIEncoding.ASCII.GetBytes(rowversionStr);

这给了我byte[18],当我将它转换为它时,Int64它给了我不同的价值。

我没有得到这个。

我想rowVersion作为参数传递给WebApiget方法。由于Byte[]不允许,所以我将其作为字符串传递

更新:

 public IHttpActionResult Get(string rowVersion, int id)
    {

        var exisitingRowVersion = long.Parse(rowVersion.Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);

        var result = new MyContext().employees.ToList().Where(x => x.Id == 2 && BitConverter.ToInt64(x.RowVersion, 0) > exisitingRowVersion);

        return Ok(result);

不明白为什么这个不起作用。我们基本上是在比较 long 和 long

4

2 回答 2

4

你有三个主要问题。它不应该这么复杂,但确实如此。

这是我使用的解决方案: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 < 0xFFFFFFFFFFFFFFFF0xFFFFFFFFFFFFFFFF更大。为了保持 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
于 2016-07-01T15:43:39.727 回答
1

之前,小故事:

对我来说,可以使用从“AAAxxx==”转换为字节数组Convert.FromBase64String,反之亦然。

很长的故事:

在我的情况下,我从 SQL 2016 DB 获取复杂对象的输出,包括来自使用 FOR JSON PATH 的查询的 RowVersion 和SQL 将 RowVersion 转换为字符串基数 64 并将日期转换为字符串并直接使用该输出作为结果,但是当用作 MVCx 或 WCF 中的请求负载时失败,因为它们适用于 rowversion 转换,例如从对象序列化到 JSON 的输出,如 WCF 或 MVCx:rowversion 作为字节数组和日期作为/日期(###)。为了从 JSON-SQL 的输出转换为 JSON-MVC-WCF 兼容,我必须使用扩展在服务器中输出之前转换响应,在这种情况下,RowVersion 的示例值为 AAAxxx== 到 [0 ,0,0,#,#,#] 并为此传递 AAAxxx== 作为 Convert.FromBase64String 的参数并迭代字节数组以输出预期结果。

于 2018-04-16T17:48:02.327 回答