【问题标题】:Move Async/Await JSON deserialize result to another function将 Async/Await JSON 反序列化结果移动到另一个函数
【发布时间】:2021-05-05 08:50:42
【问题描述】:

我有这个功能,最多可以输入 10 个项目作为输入列表

public async Task<KeyValuePair<string, bool>[]> PayCallSendSMS(List<SmsRequest> ListSms)
{
    List<Task<KeyValuePair<string, bool>>> tasks = new List<Task<KeyValuePair<string, bool>>>();

    foreach (SmsRequest sms in ListSms)
    {
        tasks.Add(Task.Run(() => SendSMS(sms)));
    }

    var result = await Task.WhenAll(tasks);
    return result;
}

在这个函数中,我 await 用于下载一些 JSON,并在完成反序列化后对其进行反序列化。

public async Task<KeyValuePair<string, bool>> SendSMS(SmsRequest sms)
 {
    //some code
      using (WebResponse response = webRequest.GetResponse())
    {
        using (Stream responseStream = response.GetResponseStream())
        {
            StreamReader rdr = new StreamReader(responseStream, Encoding.UTF8);
            string Json = await rdr.ReadToEndAsync();
            deserializedJsonDictionary = (Dictionary<string, object>)jsonSerializer.DeserializeObject(Json);
        }
    }
    //some code
     return  GetResult(sms.recipient);
 }

public KeyValuePair<string, bool> GetResult(string recipient)
{
    if (deserializedJsonDictionary[STATUS].ToString().ToLower().Equals("true"))
    {
        return new KeyValuePair<string, bool>(recipient, true);
    }
    else // deserializedJsonDictionary[STATUS] == "False"
    {
        return new KeyValuePair<string, bool>(recipient, false);
    }
}

我的问题是return GetResult(); 部分,其中deserializedJsonDictionary 为空(并且因为json 还没有完成下载)。

但我不知道如何解决它

我尝试使用ContinueWith,但它对我不起作用。

我愿意接受对我的原始代码和/或解决方案设计的任何更改

【问题讨论】:

  • 你为什么使用WebRequest? (WebRequest 是古老的,不应使用)。为什么不使用支持 real 异步 IO 的HttpClient?您也不应该为此使用Task.Run
  • if (deserializedJsonDictionary[STATUS].ToString().ToLower().Equals("true"))
  • deserializedJsonDictionary 不为空“因为 JSON 尚未完成下载” - 这是因为您错误地反序列化 JSON。您需要查看实际的响应正文(请将其发布在您的问题中,以便我们查看)。 Newtonsoft.Json 库非常宽容,默认情况下,如果 JSON 文本与请求的类型不匹配,它将返回 null 而不是抛出描述性异常。

标签: c# json async-await task streamreader


【解决方案1】:
  • 不相关的提示:不要滥用 KeyValuePair&lt;&gt;,改用 C# 7 值元组(尤其是因为它们更容易阅读)。
  • 使用foreach 循环来构建List&lt;Task&gt; 很好——尽管使用.Select() 会更简洁。我在回答中使用了这种方法。
  • 但不要将Task.Runancient WebRequest (HttpWebRequest) 类型一起使用。而是使用完全支持异步 IO 的 HttpClient
  • 此外,您应该遵守 .NET 命名约定:
    • 所有异步方法都应该有 Async 有一个方法名后缀(例如,PayCallSendSMS 应该命名为 PayCallSendSmsAsync)。
    • 超过 2 个字符的首字母缩写词和首字母缩写词应使用 PascalCase,而不是 CAPS,因此请使用 Sms 而不是 SMS
    • 对参数和本地变量使用camelCase,而不是PascalCase - 并且List 是冗余前缀。 ListSms 的更好名称是 smsRequests,因为它的类型是 List&lt;SmsRequest&gt;)。
  • 一般来说,参数应该使用所需的最不具体的类型来声明 - 尤其是 集合参数,考虑将它们键入为IEnumerable&lt;T&gt;IReadOnlyCollection&lt;T&gt; 而不是T[]List&lt;T&gt;,等等)。
  • 您需要首先检查来自远程服务器的响应实际上是 JSON 响应(而不是 HTML 错误消息或 XML 响应)并且具有预期的状态代码 - 否则您将尝试反序列化一些不是JSON。
  • 也考虑支持CancellationToken(我的答案中不包含这部分,因为它会增加太多视觉噪音)。
  • 始终使用Dictionary.TryGetValue,而不是盲目地假设字典索引器会匹配。

public async Task< IReadOnlyList<(String recipient, Boolean ok)> > PayCallSendSmsAsync( IEnumerable<SmsRequest> smsRequests )
{
    using( HttpClient httpClient = this.httpClientFactory.Create() )
    {
        var tasks = smsRequests
            .Select(r => SendSmsAsync(httpClient, r))
            .ToList(); // <-- The call to ToList is important as it materializes the list and triggers all of the Tasks.

        (String recipient, Boolean ok)[] results = await Task.WhenAll(tasks);
        return results;
    }
}

private static async Task<(String recipient, Boolean ok)> SendSmsAsync(HttpClient httpClient, SmsRequest smsRequest)
{
    using (HttpRequestMessage request = new HttpRequestMessage( ... ) )
    using (HttpResponseMessage response = await httpClient.SendAsync(request).ConfigureAwait(false))
    {
        String responseType = response.Content.Headers.ContentType?.MediaType ?? "";
        if (responseType != "application/json" || response.StatusCode != HttpStatusCode.OK)
        {
            throw new InvalidOperationException("Expected HTTP 200 JSON response but encountered an HTTP " + response.StatusCode + " " + responseType + " response instead." );
        }

        String jsonText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
        
        Dictionary<String,Object> dict = JsonConvert.DeserializeObject< Dictionary<String,Object> >(jsonText);

        if(
            dict != null &&
            dict.TryGetValue(STATUS, out Object statusValue) &&
            statusValue is String statusStr &&
            "true".Equals( statusStr, StringComparison.OrdinalIgnoreCase )
        )
        {
            return ( smsRequest.Recipient, ok: true );
        }
        else
        {
            return ( smsRequest.Recipient, ok: false );
        }
    }
}

【讨论】:

  • 我使用了HttpClient httpClient = new HttpClient(); 而不是HttpClient httpClient = this.httpClientFactory.Create(),因为我的项目是4.6.1,httpClientFactory 来自.NET core AFAIK,但我得到一个错误基础连接已关闭 ,我该如何解决? @戴
  • 添加了 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;还是一样
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-18
  • 2011-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多