在ASP.Net Core,是否可以开始流式传输JSON结果
本文关键字:开始 传输 结果 JSON Net ASP Core 是否 | 更新日期: 2023-09-27 18:16:05
我使用的是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
头交付)。
[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中,你应该包含偏移量和限制的参数,以允许客户端以任何大小的块步进并检索结果集,你可以用它来决定什么看起来足够灵敏。然后在你的客户端,你需要一个ajax调用你的api的循环,可能使用jquery,并保持一个又一个页面的循环,将结果添加到客户端的绑定集合或创建ui元素,或其他,直到结果返回空。
或者,如果一次显示全部10k条记录不是必要的,您可以简单地对结果进行分页,并提供一个在页面中分步执行的接口。我用过的一个是Sakura在git hub上的:PagedList