我正在开发一个解决方案,其中我有一个有效负载列表,这些负载需要POSTED到一个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
?
答案 0 :(得分:1)
将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
正在为此目的而设计。
答案 1 :(得分:1)
我喜欢使用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();