Jakub Koralewski
Jakub Koralewski

Reputation: 693

How to await multiple possibly uninitialized Tasks in C#?

I have an API POST endpoint creating a resource, that resource may have multiple relationships. To make sure the resource is created with valid relationships first I need to check if the given IDs exist. There are multiple such relations, and I don't want to await each sequentially. Here's my code:

[HttpPost]
public async Task<ActionResult<Person>> PostPerson(Person person)
{
    ValueTask<Person> master, apprentice;
    ValueTask<Planet> planet;
    ValueTask<Models.LifeFormType> lifeFormType;
    if (person.MasterId.HasValue)
    {
        master = _context.People.FindAsync(person.MasterId);
    }
    

    if (person.ApprenticeId.HasValue)
    {
        apprentice = _context.People.FindAsync(person.ApprenticeId);
    }

    if (person.FromPlanetId.HasValue)
    {
        planet = _context.Planets.FindAsync(person.FromPlanetId);
    }

    if (person.LFTypeId.HasValue)
    {
        lifeFormType = _context.LifeFormTypes.FindAsync(person.LFTypeId);
    }
    List<ValueTask> tasks = new List<ValueTask> {master, apprentice, planet, lifeFormType};

    // if the above worked I'd process the tasks as they completed and throw errors
    // if the given id was not found and such
    
    _context.Attach(person);
    // _context.People.Add(person);
    await _context.SaveChangesAsync();

    return CreatedAtAction("GetPerson", new { id = person.Id }, person);
}

As shown here I want to await the list of [master,apprentice,planet,lifeFormType] as they complete, but I get an error during the creation of the list that Local variable 'master' might not be initialized before accessing. So I tried in each check if the resource has that value to create an else block and somehow add a ValueTask.CompletedTask like so:

if (person.MasterId.HasValue)
{
    master = _context.People.FindAsync(person.MasterId);
}
else
{
    master = ValueTask.CompletedTask;
}

but then I get an error saying that Cannot convert source type 'System.Threading.Tasks.ValueTask' to target type 'System.Threading.Tasks.ValueTask<Models.Person>'.

How to do this? I guess I'll just await each and every request for now.

Upvotes: 2

Views: 266

Answers (1)

Aluan Haddad
Aluan Haddad

Reputation: 31803

You can avoid this by initializing master at the declaration site.

The easiest way is using the default keyword.

ValueTask<Person> master = default;

Upvotes: 6

Related Questions