65.9K
CodeProject 正在变化。 阅读更多。
Home

案例研究:增强 JavaScript 和 C# 之间的互操作性,优化 Blazor 中的批量查询

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.03/5 (5投票s)

2024年9月3日

CPOL

3分钟阅读

viewsIcon

4069

通过增强 JavaScript 和 C# 之间的互操作性来优化 Blazor 中的海量查询,重点是性能改进。

事实上,在生产环境中使用 NuGet 包比最初看起来压力更大。当其他人开始使用你的包时,他们会开始报告问题或请求新功能。此外,我经常在我的其他项目中使用这些包,无论是个人项目还是为我工作的公司。

在我的用于从 Razor 处理 IndexedDB 的 NuGet 包中,主要用于 Blazor WebAssembly 应用程序,我在一个个人项目中遇到了挑战:一个展示我的电影收藏的网站 https://movies.sergiortizgomez.com/. 我想将电影、演员和导演之间的所有关系存储在我的本地数据库中。这导致一个表格仅电影和演员之间的关系就有超过 27,237 条记录,而电影和导演之间的关系则有 489 条记录。

对于电影和导演表,数据检索相当快,仅需一秒多一点。但是,对于电影和演员表,网站会冻结 29 秒,然后才能再次响应。 这是一个无法接受的延迟!

识别问题

我做的第一件事是使用 Stopwatch 对象测量执行代码所需的时间。 这帮助我确定问题在于 DrUalcman-BlazorIndexedDb NuGet 的数据检索过程,该过程花费了超过 26 秒的时间才能返回数据列表。

确定错误的来源

我最初怀疑问题可能出在 JavaScript 方面 - 我喜欢的语言(带有一丝讽刺)。 因此,我尝试优化 JavaScript 代码,同时继续从 C# 测量结果。 虽然我设法略微减少了时间,但仍然太慢了。

然后,我决定直接测量 JavaScript 从 IndexedDB 读取数据所需的时间。 令我惊讶的是,我发现读取 27,237 条记录花费不到一秒,有时甚至不到半秒。 这排除了 JavaScript 作为问题的根源。

开始寻找解决方案

知道 JavaScript 不是罪魁祸首后,我专注于如何将数据发送到 C#。

首先,我改变了我在 C# 中的方法。 而不是尝试直接接收对象

List<TModel> data = await jsRuntime.GetJsonResult<List<TModel>>("MyDb.Select", Setup.Tables.GetTable<TModel>(), Setup.DBName, Setup.Version, Setup.ModelsAsJson);

我开始请求数据作为 JsonElement。 这将时间从 26 秒减少到 14 秒,表明问题更多在于 C# 如何处理数据,而不是 JavaScript。 但是,从 JsonElement 转换为所需的对象会将时间恢复到 25 秒,这仍然是无法接受的。

然后我想:为什么不直接从 JavaScript 发送文本,然后在 C# 中反序列化呢? 这种改变将时间减少到 20 秒,但仍然不够。

我考虑在 JavaScript 中压缩数据以减少发送的信息量,但是此解决方案需要在 JavaScript 和 C# 中实现大量附加代码,这不是理想的选择。

找到解决方案

最后,我想出了一个主意:为什么不从 JavaScript 发送字节而不是文本呢?

async function JsonToBytes(data) {
    const jsonString = JSON.stringify(data);
    const encoder = new TextEncoder();
    return encoder.encode(jsonString);
}

以及从 IndexedDB 读取数据时

request.onsuccess = async () => {

db.close();
const data = await JsonToBytes(request.result);
resolve(data);
};

这使我可以在 C# 中执行以下操作

byte[] dataBytes = await jsRuntime.InvokeAsync<byte[]>(
    "MyDb.Select",
    Setup.Tables.GetTable<TModel>(),
    Setup.DBName,
    Setup.Version,
    Setup.ModelsAsJson);

List<TModel> data = JsonSerializer.Deserialize<List<TModel>>(
    dataBytes,
    new JsonSerializerOptions {
        PropertyNameCaseInsensitive = true,
        AllowTrailingCommas = true,
        ReadCommentHandling = JsonCommentHandling.Skip
    });

JsonSerializerOptions 中的其他选项也有助于提高反序列化性能。 结果,时间显着改善了

  • 从 JavaScript 检索数据不会超过 1 秒,即使对于 27,237 条记录也是如此。
  • 将数据转换为所需的列表花费不到 9 秒。 尽管可以通过 Microsoft 改进此时间,但我对结果感到满意。
  • 以前大约需要一秒或更长时间的其他查询现在在不到半秒的时间内执行。 这真是一项重大的改进!

结论

这就是我进行调查并优化代码的方式,该代码现在在我的 NuGet 包的最新版本中可用。

获得的经验教训是

  • 最好从 JavaScript 向 C# 发送字节,而不是 JSON 对象。
  • 在 C# 中从 byte[] 反序列化比从字符串反序列化快得多。

如果您需要提高响应速度或在 JavaScript 和 C# 之间工作时最大限度地减少用户界面阻塞,这些点尤其重要。 我在这里没有展示太多代码,因为这只是个人经验。 如果您想详细了解改进情况,可以在此 存储库的提交中 查看更改前后的代码。

© . All rights reserved.