C#中FOR循环中的多个和从属异步REST API调用(链接)

时间:2016-09-29 03:33:04

标签: c# multithreading asynchronous async-await task

我正在开发一个解决方案,其中我有一个有效负载列表,这些负载需要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

这样的东西

2 个答案:

答案 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();