我从数据库中获取
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
作为参数传递给 WebApi
get 方法。由于 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);
不明白为什么这个不起作用。我们基本上是在比较长与长
您面临三个主要问题。本来不应该这么复杂,但事实就是如此。
这是我使用的解决方案:Timestamp.cs。这更容易。我会在最后举一个例子。
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
是大尾数还是小尾数,具体取决于您运行的系统上
ulong
是大尾数还是小尾数。您可以亲自看看这个。无论您在什么系统上运行,您都需要一个始终为大尾数的转换:
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.二进制比较是无符号的
0x0FFFFFFFFFFFFFFF < 0xFFFFFFFFFFFFFFFF
时,
0xFFFFFFFFFFFFFFFF
更大。为了保持 SQL Server 处理它的相同含义,您必须使用 ulong
而不是
long
。否则,
0xFFFFFFFFFFFFFFFF
会变成 -1L
,而不是 SQL Server 所认为的 ulong.MaxValue
。当然,在使用
timestamp
列的高位之前必须发生 9 万亿次事情,但是您可以使用相同的代码来比较以其他方式生成的两个
binary(8)
时间戳。重要的是复制 SQL Server 的比较行为。最干净的解决方案
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
对我来说,可以使用
Convert.FromBase64String
将“AAAxxx==”转换为字节数组,反之亦然。
长话短说:
在我的例子中,我从 SQL 2016 DB 获取复杂对象的输出,包括来自使用 FOR JSON PATH 的查询的 RowVersion 和
SQL 将字符串基数 64 中的 RowVersion 和日期转换为字符串,并直接使用该输出作为结果,但何时在 MVCx 或 WCF 中用作请求有效负载会失败,因为它们与 rowversion 转换配合得很好,就像从对象序列化为 JSON 的输出(如 WCF 或 MVCx 那样):rowversion 作为字节数组,日期作为 /Date(###)。 为了将 JSON-SQL 的输出转换为兼容 JSON-MVC-WCF 的格式,我必须使用扩展在服务器中的输出之前转换响应,该扩展在本例中将 RowVersion 的示例值 AAAxxx== 转换为 [0 ,0,0,#,#,#] 并为此传递 AAAxxx== 作为 Convert.FromBase64String 的参数并迭代字节数组以输出预期结果。
byte[8]
/
timestamp
的 rowversion
转换为相同的格式以进行比较。例如。乐观并发检查你会看到这样的参数。
@p3='0x0000000005B7566B'
在深入研究 EF Core 的源代码但没有找到他们在哪里做之后,我最终发现了这个非常简单的方法:
Convert.ToHexString(order.RowVersion)
// gives 8 bytes
Convert.FromHexString("0000000005B7566B") // don't include the 0x
PS。我从来没有真正理解这些“AAAxxx==”时间戳。这对任何人来说都是一种有用的格式吗?