繁体   English   中英

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM