C# asp.net 将长 属性 的对象传递给前端会改变它的值

C# asp.net passing an object with a long property to front end changes it's value

我正在构建一个带有 react.js 前端(虽然我很确定这与问题无关)和 asp.net 后端(针对 net5.0)的应用程序,不确定这是否相关)。当我调用后端 API 时,我得到一个对象,该对象部分包含它根据传入的数据生成的 ID,该 ID 在 C# 中为 long 类型(64 位 int)。我看到的行为是 C# 中的变量和从前端响应读取的变量不同。它们似乎在大约 16-17 位数字后漂移。

这是预期的吗?有什么办法可以绕过它吗?

复制代码/我看到的图片:

C#

[HttpPost]
[Route("test")]
public object TestPassingLongInObject()
{
    /* actual logic omitted for brevity */

    var rv = new
    {
        DataReadIn = new { /* omitted */ },
        ValidationResult = new ValidationResult(),
        GeneratedID =  long.Parse($"922337203{new Random().Next(int.MaxValue):0000000000}") // close to long.MaxValue
    };

    Console.WriteLine($"ID in C# controller method: {rv.GeneratedID}");

    return rv;
}

控制台输出:ID in C# controller method: 9223372030653055062

Chrome 开发工具:

当我尝试在前端访问 ID 时,我得到了以 000 而不是 062 结尾的错误 ID。

编辑 1:有人认为这是因为 JavaScript 的 Number.MAX_SAFE_INTEGER 小于我传递的值。我不相信这是原因,但也许我错了,有人可以启发我。在 JS 方面,我使用 BigInt,正是因为我传递的数字对于 Number 来说太大了。问题是在我将结果解析为 JS 对象之前(除非 Chrome 自动执行此操作,导致问题中引用的图片)。

编辑 2:根据下面的答案,看起来 JS 可能在我将其解析为 BigInt 之前将值解析为数字,所以我仍然失去精度。更熟悉网络的人可以证实这一点吗?

JavaScript 引擎(显然)无法创造这么大的数字。尝试打开您的控制台并输入以下内容:

var myLong = 9223372030653055062;

console.log(myLong);

您也可以检查 9223372030653055 > Number.MAX_SAFE_INTEGER,作为您越界的指示。

JavaScript 将该值解释为 number,这是一个 64 位浮点数。如果您想保留该值,最好将其作为字符串传回。

演示问题的示例 JS:

var value = 9223372030653055062;
console.log(value);
console.log(typeof(value));

Javascript 中数字的最大大小为 2^53 - 1,即 9007199254740991。在 C# 中,long 为 2^64 - 1,更大。所以“9223372030653055062”作为 C# 长而不是 JavaScript 数字,因为它太大了。如果这个 ID 在使用 long 的数据库中使用,我建议您将它作为字符串传递给 JavaScript.

虽然原因是这个数字大于 JS 可以准确表示的数量,但您似乎被 Chrome 的 Dev Tools Preview 分散了注意力。这只是一个“有帮助”的预览,并不一定是事实。

开发工具预览显示了有用的响应视图。我假设 AJAX 响应以 application/json 的内容类型传输,因此 Chrome 有助于将 JSON 解析为 JS 对象并让您预览响应。检查“响应”选项卡,这是您的 AJAX 代码实际收到的内容。

很有可能 JSON 在您有机会对该数字使用 BigInt 之前被解析,从而限制了精度。您还没有向我们展示您的 AJAX 代码,所以这是我的最佳猜测。

唯一的解决方案是将其序列化为字符串,然后添加一个手动步骤将字符串转换为 BigInt

jsObject.generatedId = BigInt(jsObject.generatedId);

如果您将它用作 ID,那么您最好将它作为字符串保存在客户端,因为您不太可能使用它进行任何计算。