【问题标题】:rowVersion gets mapped to byte[8] in Entityframe work but when manually casting it's byte[18]rowVersion 在 Entityframe 工作中被映射到 byte[8],但是在手动转换时它是 byte[18]
【发布时间】:2015-07-01 13:47:16
【问题描述】:

我从数据库中得到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);

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

【问题讨论】:

  • 正确的方法是long.Parse("0x0000000000038B8C".Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);... 但是即使long 也没有被Javascript 完全支持,所以我不会传递long... 你可以传递出去直接string 值。
  • 基本上字符串被映射到我的 webapi 参数。字符串基本上是存储在 Sql Server 数据库中的 rowVersion 现在我想执行 this.context.employee.where(x=>(BitConverter.ToInt64(x.rowVersion,0)> rowVersionParameter))。在这里,我无法将 long 与字符串值进行比较。我所做的是尝试从字符串创建一个 byte[] 以将其与数据库值进行比较。但这行不通。当我将它转换为 byte[] 它返回 byte[18]
  • Well long.Parse("0x0000000000038B8C".Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);看起来像一个有效的答案
  • 但是你不能在查询中使用它......你不能将RowVersion类型映射到long......它似乎只与byte[]兼容。 .. 但你不能用byte[] 来做> ...例如见stackoverflow.com/q/7437970/613130
  • 添加了对原始问题的更新。还是不明白为什么 long 不拿 long 比较?

标签: c#-4.0 entity-framework-6 rowversion


【解决方案1】:

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

这是我使用的解决方案: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 的系统上。你可以see this for yourself。无论您在哪个系统上运行,您都需要一个始终为大端的转换:

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 &lt; 0xFFFFFFFFFFFFFFFF 时,0xFFFFFFFFFFFFFFFF 更大。为了保持 SQL Server 处理它的相同含义,您必须使用ulong而不是long。否则0xFFFFFFFFFFFFFFFF 变为-1L 而不是SQL Server 认为的ulong.MaxValue

诚然,在使用 timestamp 列的高位之前,必须发生 9 万亿次事情,但您可以使用相同的代码来比较两个以其他方式生成的 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,你就不会出错。

遗憾的是,无论您使用哪种方法,都没有在服务器端而不是客户端应用此过滤器的好方法。这就是this question 的主题。猜猜我发现了什么! A way to do this 使用实体框架 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

【讨论】:

    【解决方案2】:

    之前,短篇小说:

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

    长篇大论:

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

    【讨论】:

    • 这不应被标记为“不是答案”。首先,因为此标志 only 适用于甚至不尝试成为答案的答案。其次,因为Convert.FromBase64StringConvert.ToBase64String正是Rowversion/String转换要使用的方法。唯一的一点是,它可以解释得更广泛一些。
    猜你喜欢
    • 1970-01-01
    • 2020-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-08
    • 1970-01-01
    • 2011-08-23
    • 2014-07-21
    相关资源
    最近更新 更多