M. Akar
M. Akar

Reputation: 1865

How to chain async calls in a complex linq query

In a .NET application, I am trying to construct a DTO on the repo layer as following. However, I have a nasty async function deep down in the statement. How should I chain the async calls?

var subtopics; // I have a subtopics array

var topicModel = new TopicModel
{
    Id = 0,
    SubtopicModels = subtopics
        .Select(subtopic => new SubtopicModel
        {
            Id = 0,
            VideoModels = subtopic.Videos // simple await on this line doesn't work
                .OrderBy(video => video.OrderInSubtopic)
                .Select(async video => new VideoModel
                {
                    Id = 0,
                    VideoLeftAtSeconds = (await _db.VideoActivities
                        .FirstOrDefaultAsync(activity => activity.UserId == userId && activity.VideoId == videoId))
                        .LeftAtSeconds
                }).ToList()
        }).ToList(),
};

Upvotes: 0

Views: 341

Answers (1)

Lewis Hazell
Lewis Hazell

Reputation: 76

The problem is that the lambda given to the deepest Select (.Select(async video => ...) is going to return a Task<T> (I assume Task<int> but not sure from the context).

Select doesn't understand how to use a Task and will just pass it through as is. You can convert these in bulk by using WhenAll (1) but you would have to make extra provisions on the database connection, as this will execute multiple queries in parallel. (2)

The most simple way in this instance is probably to scrap the LINQ and use foreach, like this:

var subtopics;

var topicModel = new TopicModel
{
    Id = 0,
    SubtopicModels = new List<SubtopicModel>()
};

foreach(var subtopic in subtopics)
{
    var subtopicModel = new SubtopicModel
    {
        Id = 0,
        VideoModels = new List<VideoModel>()
    };

    foreach(var video in subtopic.Videos.OrderBy(video => video.OrderInSubtopic))
    {
        subtopicModel.VideoModels.Add(new VideoModel
        {
            Id = 0,
            VideoLeftAtSeconds = (await _db.VideoActivities
                        .FirstOrDefaultAsync(activity => activity.UserId == userId && activity.VideoId == videoId))
                        .LeftAtSeconds
        });
    }

    topicModel.SubtopicModel.Add(subtopicModel);
}

Upvotes: 4

Related Questions