Enumerable.SelectMany throws Error CS0411 - cannot be inferred from the usage

107 Views Asked by At

I'm using c#, .net 6.0

I'm trying to convert IEnumerable method and ot's caller method to work asynchronously.

I have a code that looks something like that:

    public IEnumerable<MyUser> getUsers()
{
    return AccountById
        .Keys
        .SelectMany(accountId => {
            try
            {
                return getUsersFor(accountId)
                    .Select(x => new MyUser(x,
                        SetAccountUniqueName(AccountById[accountId].Name,
                            AccountById[accountId].Id)));
            }
            catch (Exception ex)
            {
                _log.Error(ex);
                return Enumerable.Empty<MyUser>();
            }
        });
}

public IEnumerable<User> getUsersFor(string accountId)
{
    ListUsersResponse usersResponse;
    string marker = null;
    do
    {
        using (var myClient = new ServiceClient(accountId))
        {
            usersResponse =
                myClient.ListUsersAsync(
                        new ListUsersRequest { Marker = marker, MaxItems = MAX_ITEMS })
                    .Result;
            foreach (var user in usersResponse.Users)
            {
                yield return user;
            }
        }

        marker = usersResponse.Marker;
    } while (usersResponse.IsTruncated);
}

I converted getUsers() and getUsersFor() to work asynchronously.

I tried this code:

    public async Task<List<MyUser>> GetUsersAsync()
    {
        return await AccountById
            .Keys
            .SelectMany(async accountId =>
            {
                try
                {
                    await foreach (var user in GetUsersForAsync(accountId))
                    {
                        var u = new MyUser(user,
                            SetAccountUniqueName(AccountById[accountId].Name,
                                AccountById[accountId].Id));
                        return u;
                    }
                }
                catch (Exception ex)
                {
                    _logger.Error(ex, $"Failed to get account {accountId} users list.");
                    return Enumerable.Empty<MyUser>();
                }
            });
    }

    public async IAsyncEnumerable<User> GetUsersForAsync(string accountId)
    {
        ListUsersResponse usersResponse;
        string marker = null;
        do
        {
            using (var myClient = new ServiceClient(accountId))
            {
                usersResponse =
                    await myClient.ListUsersAsync(
                            new ListUsersRequest { Marker = marker, MaxItems = MAX_ITEMS });
                foreach (var user in usersResponse.Users)
                {
                    yield return user;
                }
            }

            marker = usersResponse.Marker;
        } while (usersResponse.IsTruncated);
    }

But I get this error:

The type arguments for method 'Enumerable.SelectMany<TSource, TResult>(IEnumerable, Func<TSource, IEnumerable>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

1

There are 1 best solutions below

0
Svyatoslav Danyliv On

You should not use LINQ for asynchronous operations. SelectMany of asynchronous call returns Task which is wrong way if you do not await them.

Simple foreach will work in your case, don't complicate things.

public async Task<List<MyUser>> GetUsersAsync()
{
    var result = new List<MyUser>();
    foreach (var accountId in AccountById.Keys)
    {
        try
        {
            await foreach (var user in GetUsersForAsync(accountId))
            {
                var u = new MyUser(user,
                    SetAccountUniqueName(AccountById[accountId].Name,
                        AccountById[accountId].Id));
                result.Add(u);
            }
        }
        catch (Exception ex)
        {
            _logger.Error(ex, $"Failed to get account {accountId} users list.");
        }    
    }

    return result;
}