[英]In ASP.Net Core, is it possible to start streaming JSON results?
我正在使用 ASP.Net Core WebAPI。
我有一种方法可以一次从数据库中检索 10000 个结果,但我注意到“等待”需要 1.17 秒,实际传输需要 0.3 秒(基于 Chrome 的网络图)。
将来自数据库(postgres)的结果通过 DataReader 进行迭代并转换为结构体,添加到列表中,最终作为 JsonResult 返回。
我不知道对选项的确切期望,但我希望能够尽快开始返回以降低总请求。 我也是第一次在这个平台上这样做,所以我可能没有以最好的方式做事。
[HttpGet("{turbine:int}")]
public IActionResult GetBearingTemperature(int turbine)
{
using (var connection = Database.GetConnection())
{
connection.Open();
int? page = GetPage();
var command = connection.CreateCommand();
if (page.HasValue)
{
command.CommandText = @"select turbine, timestamp, mainbearingtemperature from readings where turbine = :turbine limit 10000 offset :offset;";
command.Parameters.AddWithValue("offset", NpgsqlTypes.NpgsqlDbType.Integer, page.Value * 10000);
} else
{
command.CommandText = @"select turbine, timestamp, mainbearingtemperature from readings where turbine = :turbine limit 10000;";
}
command.Parameters.AddWithValue("turbine", NpgsqlTypes.NpgsqlDbType.Integer, 4, turbine);
var reader = command.ExecuteReader();
var collection = new List<BearingTemperature>();
if (reader.HasRows)
{
var bt = new BearingTemperature();
while (reader.Read())
{
bt.Time = reader.GetDateTime(1);
bt.Turbine = reader.GetInt32(0);
bt.Value = reader.GetDouble(2);
collection.Add(bt);
}
return new JsonResult(collection);
}
else
{
return new EmptyResult();
}
}
}
private int? GetPage()
{
if (Request.Query.ContainsKey("page"))
{
return int.Parse(Request.Query["page"]);
}
else return null;
}
struct BearingTemperature
{
public int Turbine;
public DateTime Time;
public double Value;
}
所以我知道这个问题很老,但这在 Asp.Net Core 2.2 中很有可能(甚至可能来自早期版本,因为IEnumerable<T>
被支持作为操作的返回结果)。
虽然我对 postgres 和 DataReader 并不完全熟悉,但该功能可以将结果流式传输到客户端。 根据结果的大小,追加到列表并返回整个结果会占用大量内存,而流媒体可以帮助我们避免这种情况。
这是一个操作示例,它返回一个IEnumerable<string>
流到客户端(它以块的形式发送,直到使用Transfer-Encoding: chunked
标头Transfer-Encoding: chunked
所有内容为止)。
[HttpGet]
public IEnumerable<string> Get()
{
return GetStringsFor(10000);
}
private static readonly Random random = new Random();
private IEnumerable<string> GetStringsFor(int amount)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
while (amount-- > 0)
{
yield return new string(Enumerable.Repeat(chars, random.Next(1000)).Select(s => s[random.Next(s.Length)]).ToArray());
}
}
这将确保不是所有内容都加载到内存中,而是按需发送。 当您将数据读入内存时,您将能够在您的情况下实现类似的东西,因为这是系统可以开始发送结果的一次。
private IEnumerable<BearingTemperature> ReadTemperatures(SqlDataReader reader)
{
if (reader.HasRows)
{
var bt = new BearingTemperature();
while (reader.Read())
{
bt.Time = reader.GetDateTime(1);
bt.Turbine = reader.GetInt32(0);
bt.Value = reader.GetDouble(2);
yield return bt;
}
}
yield break;
}
[HttpGet("{turbine:int}")]
public IEnumerable<BearingTemperature> GetBearingTemperature(int turbine)
{
using (var connection = Database.GetConnection())
{
<snip>
var reader = command.ExecuteReader();
return ReadTemperatures(reader);
}
}
考虑到您的数据库将执行查询并返回整个结果集,您不可能流式传输部分结果集(尽管您可以在 google 流数据库中搜索其他产品)。 您可以做的是使用分页技术与 ajax 相结合来检索总结果集的切片,并将它们组合在客户端上,以保持高响应度并创建流式查询结果的错觉。
您需要查看OFFSET
和LIMIT
子句
在您的 api 上,您将包含 offset 和 limit 的参数,以允许客户端逐步执行并以它想要的任何大小块检索结果集,您可以使用它来确定哪些看起来响应足够快。 然后在您的客户端上,您将需要对 api 的 ajax 调用进行循环,可能使用 jquery,并不断循环页面将结果添加到客户端上的绑定集合或创建 ui 元素或其他任何内容,直到结果空着回来。
或者,如果不需要一次显示整个 10k 记录,您可以简单地对结果进行分页并提供一个界面来逐步浏览这些页面。 我用于此目的的一个来自 git hub 上的 Sakura: PagedList
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.