【问题标题】:Is there an async equivalent of StreamReader's Peek method?是否有 StreamReader 的 Peek 方法的异步等效项?
【发布时间】:2020-01-06 18:04:34
【问题描述】:

我正在更新一些netstandard2.0 代码以不同步从HttpRequest.BodyStream)读取(这会在netcoreapp3.0 中引发异常,除非您设置AllowSynchronousIO 选项,这显然不是一个好主意)。

我已经转换了 JSON 反序列化部分(以使用 System.Text.Json),但在此之前,它使用 StreamReader 做了一个偷偷摸摸的 .Peek()(查看主体是对象还是数组)和我不确定在哪里可以找到一种现代的异步替代方案,并且不会消耗Stream

using var reader = new StreamReader(stream);
switch (reader.Peek()) // TODO: Find an async equivalent!
{
    case '{':
        return await JsonSerializer.DeserializeAsync<GraphQLRequest>(stream);
    case '[':
        return await JsonSerializer.DeserializeAsync<GraphQLRequest[]>(stream);
    default:
        // ...
}

【问题讨论】:

  • 没有,使用 Task.Run() 使其异步。
  • 你能不能像现在这样使用阅读器,然后await reader.ReadAsync。然后你没有阻塞可以事先检查你正在使用数组等......
  • @HansPassant 你为什么要在另一个任务中包装一个已经可用的支持异步的阅读器?
  • 在不指定类型的情况下反序列化,然后检查返回的对象并将其转换为适当的类型
  • 另一方面,您可以使用 try/catch 并尝试反序列化为第一种类型,而不是 case 语句,如果失败请尝试其他类型。不会有阻塞,没有装箱/拆箱。

标签: c# asynchronous .net-core stream streamreader


【解决方案1】:

实际上,Peek 方法首先将流读入它的缓冲区,然后从它给你值(看看dotnet runtime repo)。所以它移动了流的位置。但是,如果不读取并因此寻找流,就无法实现 peek 功能。

由于您无法返回 HttpRequestStream,因此更好的策略是读取整个内容,然后使用Utf8JsonReader 来确定请求是对象还是数组。注意Utf8JsonReader不能在异步方法中声明,所以我不得不把它放在一个单独的非异步方法中。

private static JsonTokenType GetTokenType(byte[] bytes)
{
    var reader = new Utf8JsonReader(bytes.AsSpan());
    reader.Read();
    return reader.TokenType;
}
var ms = new MemoryStream();
await Request.Body.CopyToAsync(ms);
var jsonBytes = ms.ToArray();

switch (GetTokenType(jsonBytes)) 
{
    case JsonTokenType.StartObject:
        return JsonSerializer.Deserialize<GraphQLRequest>(jsonBytes);
    case JsonTokenType.StartArray:
        return JsonSerializer.Deserialize<GraphQLRequest[]>(jsonBytes);
    default:
        // ...
}

【讨论】:

  • 太棒了,谢谢@Mayo。我的示例中没有包含的一件事是我使用了接受JsonSerializerOptions 的反序列化重载。看起来不像需要字节的重载也需要选项 obj。我可以做JsonSerializer.Deserialize&lt;GraphQLRequest[]&gt;(jsonBytes.AsSpan(), _serializerOptions)吗?
  • 是的,您可以这样做,AsSpan 不进行分配,但是它不能在异步方法中使用,因此您必须将代码移动到非异步方法。
  • 有趣。它似乎适用于我的异步方法。它会无声无息地爆发吗?我应该把它们移出去确定吗?
  • @benmccallum 对不起,我错了,可以直接作为参数传递,但不能将 Span 分配给异步方法中的变量。
【解决方案2】:

由于我们的特殊情况,我们(幸运的是!)从@davidfowl 那里得到了一些帮助,他使用PipeReader 为我们创建了一个示例,这是AdvanceTo 方法来查看令牌但不消耗流,所以我们可以直接从流中使用Deserialize

完整来源here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-03
    • 1970-01-01
    • 1970-01-01
    • 2012-11-18
    • 1970-01-01
    • 1970-01-01
    • 2014-04-06
    • 1970-01-01
    相关资源
    最近更新 更多