重用 IAsyncEnumerable 实例而无需再次迭代

Reuse a IAsyncEnumerable instance without having to iterate again

我目前正在编写这样的方法:

public async Task<UserModel> GetUserByUserIdAsync(string userId)
{
    IQueryable<UserEntity> usersQuery = BuildQueryable(userId);

    bool any = await usersQuery.ExecuteQuery().AnyAsync();
    
    if (!any) return null; // wanna do other logic in the future

    return (await usersQuery.ExecuteQuery().SingleAsync()).ToUserModel();
}

如您所见,我调用了 await usersQuery.ExecuteQuery() 两次,ExecuteQuery() 是一种迭代我的数据库的方法,可以被认为是一项昂贵的操作。有什么方法可以像我通常使用 IEnumerable<T> 那样保存我的 IAsyncEnumerable<T> 并在我的代码中重复使用它?

我考虑过使用 ToListAsync(),但我不确定这是否被认为是好的做法。我还读到我可以 return a Task<IAsyncEnumerable<T>> 并可能用它做点什么。处理这个问题的最佳方法是什么?我想实施最有效的解决方案。

为什么不简单地使用 SingleOrDefaultAsync? Assuming your entity is a reference type you can get your single item, check if it is null to handle the empty-case. Another alternative is always to convert the enumerable to a list。然后,您可以根据需要对其进行多次迭代。

如果返回的单个 UserEntity 有可能是 null,并且您想区分无实体和 one-null-entity,您可以安装 System.Linq.Async 打包并执行此操作:

public async Task<UserModel> GetUserByUserIdAsync(string userId)
{
    IQueryable<UserEntity> usersQuery = BuildQueryable(userId);

    var (userEntity, exists) = await usersQuery
        .AsAsyncEnumerable()
        .Select(x => (x, true))
        .FirstOrDefaultAsync();

    if (!exists) return null; // wanna do other logic in the future
    return userEntity.ToUserModel();
}

此查询利用了 ValueTuple<UserEntity, bool> 的默认值为 (null, false) 的事实。

尽管使用 AsAsyncEnumerable may not be as efficient as using the SingleOrDefaultAsync 方法,因为数据提供者可能会创建优化程度较低的执行计划。