You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# .NET6中Enumerable.SelectMany异步改造报CS0411错误求助

解决C# .NET 6异步改造中的CS0411错误

问题背景

在将同步的用户列表获取方法改造为异步形式时,出现了CS0411编译错误,错误信息如下:

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.

原同步代码

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);
}

改造后的异步代码(存在错误)

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);
}

错误原因

  1. 异步Lambda与SelectMany不兼容:传给Enumerable.SelectMany的async lambda会返回Task<MyUser>(而非预期的IEnumerable<MyUser>),因为你在await foreach中直接return u,仅返回第一个用户,Enumerable.SelectMany无法处理异步返回值,导致类型推断失败。
  2. 遍历逻辑错误:await foreach中直接return会中断遍历,丢失后续用户数据,完全偏离了原同步代码遍历所有用户的逻辑。

修复方案

方案1:使用System.Linq.Async包(推荐)

.NET 6未内置异步LINQ方法,需先安装System.Linq.Async NuGet包,之后可使用异步版LINQ方法实现逻辑:

using System.Linq;

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

方案2:手动遍历(无需额外依赖)

若不想引入外部包,可手动遍历每个账号,异步收集所有用户:

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

补充说明

  • GetUsersForAsync的实现是正确的,它通过async IAsyncEnumerable<User>完成了异步分批获取用户的逻辑,无需修改。
  • 方案1的异步LINQ语法与同步LINQ一致,适合复杂查询场景;方案2逻辑直观,适合简单场景,无额外依赖。

内容的提问来源于stack exchange,提问作者Amir M

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 16:05:56