将Async/AwaitJSON反序列化结果移动到另一个函数
c#
我有这个功能,最多可以获取 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但它对我不起作用。
我愿意接受对我的原始代码和/或解决方案设计的任何更改
回答
- 不相关的提示:不要滥用
KeyValuePair<>,而是使用 C# 7 值元组(尤其是因为它们更容易阅读)。 - 使用
foreach循环来构建 aList<Task>很好 - 尽管使用它可以更简洁.Select()。我在回答中使用了这种方法。 - 但不要
Task.Run与古老的WebRequest(HttpWebRequest) 类型一起使用。而是使用HttpClient它完全支持异步 IO。 - 此外,您应该遵守 .NET 命名约定:
- 所有异步方法都应该有
Async一个方法名后缀(例如PayCallSendSMS应该命名PayCallSendSmsAsync)。 - 长度超过 2 个字符的首字母缩略词和首字母缩写词应使用 PascalCase,而不是
CAPS,因此请使用Sms代替SMS。 - 使用
camelCase, 不适PascalCase用于参数和局部变量 - 并且List是一个冗余前缀。更好的名称ListSms是smsRequests因为它的类型是List<SmsRequest>)。
- 所有异步方法都应该有
- 一般来说,参数应该使用所需的最少特定类型来声明 -特别是集合参数,考虑将它们键入为
IEnumerable<T>或IReadOnlyCollection<T>代替T[]、List<T>等)。 - 您需要首先检查来自远程服务器的响应实际上是 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 );
}
}
}