在ASP.Net Core中,是否可以开始流式传输JSON结果?

时间:2016-11-02 06:14:15

标签: c# asp.net json postgresql asp.net-web-api

我正在使用ASP.Net Core WebAPI。

我有一个方法一次从数据库中检索10000个结果,但我注意到它需要1.17秒才能等待"和实际转移0.3s(基于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;
    }

2 个答案:

答案 0 :(得分:2)

所以我知道这个问题很旧,但是在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);
    }
}

答案 1 :(得分:0)

考虑到您的数据库将执行查询并返回整个结果集,您无法流式传输部分结果集(尽管您可以将google数据流数据库用于其他产品)。您可以做的是使用与ajax结合的分页技术来检索总结果集的切片,并在客户端上将它们组合在一起以保持响应性高并创建流式查询结果的错觉。

您想查看OFFSETLIMIT条款

在你的api上,你包含了偏移和限制的参数,以允许客户端逐步检索并检索它想要的任何大小的块结果集,你可以使用它来确定看起来足够响应的内容。然后在你的客户端上,你需要循环调用你的api的ajax,可能使用jquery,并继续循环页面,将结果添加到客户端的绑定集合或创建ui元素,或者其他任何内容,直到结果回来了。

或者,如果一次显示整个10k记录是不必要的,您可以简单地分页结果并提供一个界面来逐步浏览页面。我用于此目的的一个是来自Sakura on git hub:PagedList