.Net Core Web API 分页列表 - 源“IQueryable”的提供程序未实现“IAsyncQueryProvider”

Sam*_*i-L 1 c# entity-framework-core asp.net-core-webapi

在我的其他问题没有得到答案后,我做了一个不同的实现,这次是两个列表的并集而不是 Automapper 映射投影。

public async Task<ActionResult<IEnumerable<MemberDto>>> GetMembersList([FromQuery] UserParams userParams)
{
    var members = await _unitOfWork.UserRepository.GetMembersAsync(userParams);

    Response.AddPaginationHeader(members.CurrentPage, members.PageSize,
        members.TotalCount, members.TotalPages);

    return Ok(members);
}
Run Code Online (Sandbox Code Playgroud)

存储库:

    public async Task<PagedList<MemberDto>> GetMembersAsync(UserParams userParams)
    {
        var members1 = await _context.Users
        .Where(x => x.Photos.Any(y => y.Url.Substring(0, 4) == "http"))
            .Select(m => new MemberDto
            {
                Id = m.Id,
                UserName = m.UserName,
            }).ToListAsync();
    
        var member2 = await _context.Users
        .Where(x => x.Photos.Any(y => y.Url.Substring(0, 4) != "http"))
            .Select(m => new MemberDto
            {
                Id = m.Id,
                UserName = m.UserName,
            }).ToListAsync();
    
        var members = members1.Union(member2).OrderBy(x => x.DisplayName).ToList();
        var query = members.AsQueryable();
query = query.Where(u => u.UserName == "John");
        return await PagedList<MemberDto>.CreateAsync(query, userParams.PageNumber, userParams.PageSize);
    }
Run Code Online (Sandbox Code Playgroud)

和分页列表类:

public static async Task<PagedList<T>> CreateAsync(IQueryable<T> source, int pageNumber, 
    int pageSize)
{
    var count = await source.CountAsync();
    var items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync();
    return new PagedList<T>(items, count, pageNumber, pageSize);
}
Run Code Online (Sandbox Code Playgroud)

但是当我发送请求时出现以下错误:

System.InvalidOperationException:源“IQueryable”的提供程序未实现“IAsyncQueryProvider”。只有实现“IAsyncQueryProvider”的提供程序才能用于实体框架异步操作。

Stackoverflow 中的一些答案提供了使用 View() 的解决方法,该方法仅用于 MVC。

Iva*_*oev 6

所有 EF Core 异步可查询扩展仅适用于 EF CoreIQueryable实现。虽然ToList()将所有数据加载到内存中,但它AsQueryable()是 LINQ to ObjectsIQueryable实现,不能与 EF Core 扩展方法一起使用。

因此解决方案是删除所有ToList/await ToListAsync()调用,从而保留结果 EF Core IQueryable

据我了解,问题是 EF Core 无法转换查询,因此您正在执行它并将结果放入内存中。所以这是一种解决方法,但是查询无法在服务器端有效地分页,因此不需要异步处理。您应该做的是创建并使用Create像这样的同步方法重载

public static PagedList<T> Create(IEnumerable<T> source, int pageNumber, 
    int pageSize)
{
    var count = source.Count();
    var items = source.Skip((pageNumber - 1) * pageSize).Take(pageSize);
    return new PagedList<T>(items, count, pageNumber, pageSize);
}
Run Code Online (Sandbox Code Playgroud)

并修改代码以使用它而不是CreateAsync

// original code...
var members = members1.Union(member2).OrderBy(x => x.DisplayName).ToList();
var query = members //.AsQueryable() not needed anymore
    .Where(u => u.UserName == "John");
return PagedList<MemberDto>.Create(query, userParams.PageNumber, userParams.PageSize);
Run Code Online (Sandbox Code Playgroud)

但这只能作为最后的手段。在此之前,最好尝试使 EF Core 查询可翻译。在这种特殊情况下,我猜问题出在Union操作员(或其部件内部的某些东西)上,通过示例可以轻松消除该问题(看起来Union没有必要),但我想这不是真实情况。如果您提供一个更现实的示例以及目标 EF Core 版本,我们可以看看是否可以做一些事情(不幸的是 EF Core 查询翻译仍然是试错游戏)。