[英]Multiple and dependent asynchronous REST API calls (chaining) in a FOR loop in C#
我正在研究一种解决方案,其中我有一个需要发布到一个 API 端点的有效负载列表。
有效载荷结构看起来像这样,我有成千上万个这样的有效载荷的IList
-
{
"Body": [{
"Payload": {
"emailaddress1": "test@email.com",
"initials": "RP",
"lastname": "Patil",
"firstname": "Patil"
},
"EntityName": "person",
"Url": "https://<someurl>/api/v1/persons",
"CollectionName": "persons",
"Method": "POST"
},
{
"Payload": {
"study_name": "Chemistry",
"teacher_name": "John Doe"
},
"EntityName": "study",
"Url": "https://<someurl>/api/v1/studies",
"CollectionName": "studies",
"Method": "POST"
}]
}
现在,需要首先创建此有效负载中的第一个对象,即person
,当我收到该实体的 ID 时,我将该实体添加到第二个有效负载中,即“研究”,之后对象变成这样 -
{
"Payload": {
"person_id" : <newly_generated_id>
"study_name": "Chemistry",
"teacher_name": "John Doe"
},
"EntityName": "study",
"Url": "https://<someurl>/api/v1/studies",
"CollectionName": "studies",
"Method": "POST"
}
目前,我正在以旧的传统方式执行此操作,其中我遍历IList
并使用async await
。 等待第一个 API 调用的响应,然后调用第二个 API。 然而, foreach
是单线程的,创建这么多实体需要很长时间。
foreach(var payload in payloads)
{
var personObj = payload["Body"].Where(obj => obj["EntityName"].ToString().Equals("person")).FirstOrDefault();
var studyObj = payload["Body"].Where(obj => obj["EntityName"].ToString().Equals("study")).FirstOrDefault();
var personId = await _apiService.AddPerson(personObj);
if(personId != null){
studyObj["person_id"] = personId;
var studyId = await _apiService.AddStudy(studyObj);
//and more code.. this is just a sample code that demonstrates my implementation
}
}
这很有效,但是我正在阻止所有线程,直到在我们的系统中创建了所有数千人和他们的研究。 可以通过在每次迭代中产生单线程来实现吗? 或类似的实现?
任何人都可以指导我针对这种特定情况采取更好的方法吗? 类似Parallel.ForEach
东西?
将foreach
内部的逻辑提取到一个异步方法中,并在循环中调用此方法而无需等待。
仅在循环整个列表后才等待结果
var allTasks = new List<Task>();
foreach(var payload in payloads)
{
allTasks.Add(SendPayloadAsync(payload));
}
await Task.WhenAll(allTasks);
private async Task SendPayloadAsync(Payload payload)
{
var personObj =
payload["Body"].FirstOrDefault(obj => obj["EntityName"].ToString().Equals("person"));
var studyObj =
payload["Body"].FirstOrDefault(obj => obj["EntityName"].ToString().Equals("study"));
var personId = await _apiService.AddPerson(personObj);
if(personId != null)
{
studyObj["person_id"] = personId;
var studyId = await _apiService.AddStudy(studyObj);
}
}
这种方法将只使用一个线程。
您不需要并行发送每个payload
,创建仅等待服务响应的新线程是浪费资源。
async-await
就是为此目的而设计的。
我喜欢使用 Microsoft 的 Reactive Framework (NuGet "System.Reactive") 的想法。 然后你可以这样做:
Func<JObject, string, JToken> getToken =
(p, t) => p["Body"].Where(obj => obj["EntityName"].ToString() == t).FirstOrDefault();
Func<JToken, string, JToken> setPersonId = (j, t) =>
{
j["person_id"] = t;
return j;
};
var query =
from payload in payloads.ToObservable()
let personObj = getToken(payload, "person")
let studyObj = getToken(payload, "study")
from personId in Observable.FromAsync(() => _apiService.AddPerson(personObj))
where personId != null
from studyId in Observable.FromAsync(() => _apiService.AddPerson(setPersonId(studyObj, personId)))
select new { personObj, studyObj, };
query.Subscribe();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.