Entity Framework Automapper 分页 IQueryable' 没有实现 'IAsyncQueryProvider

Entity Framework Automapper pagination IQueryable' doesn't implement 'IAsyncQueryProvider

我有两个实体,AppUserInterestTag:

public class AppUser : IdentityUser<int>
{
    public ICollection<InterestTag> InterestTags { get; set; }
}

public class InterestTag
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<AppUser> AppUsers { get; set; }
}

它们在 DataContext 中配置为具有直接的多对多关系

builder.Entity<AppUser>()
        .HasMany(u => u.InterestTags)
        .WithMany(ut => ut.AppUsers);

我正在尝试制作一种方法,通过用户的用户名获取用户的所有 InterestTags,然后将 InterestTag 投影到 InterestTagDto 并对结果进行分页。

InterestTagDto:

public class InterestTagDto
{
    public int Id { get; set; }
    public string Name { get; set; }
}

映射配置为

CreateMap<InterestTag, InterestTagDto>();

PagedList class:

public class PagedList<T> : List<T>
{
    public PagedList(IEnumerable<T> items, int count, int pageNumber, int pageSize)
    {
        CurrentPage = pageNumber;
        TotalPages = (int)Math.Ceiling(count / (double)pageSize);
        PageSize = pageSize;
        TotalCount = count;
        AddRange(items);
    }

    public int CurrentPage { get; set; }
    public int TotalPages { get; set; }
    public int PageSize { get; set; }
    public int TotalCount { get; set; }

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

我目前的方法:

    public async Task<PagedList<InterestTagDto>> GetUserInterestTagsAsync(string username)
    {
        var query = (await _context.Users.Include(u => u.InterestTags).SingleOrDefaultAsync(u => u.UserName == username)).InterestTags.AsQueryable();

        return await PagedList<InterestTagDto>.CreateAsync(query.ProjectTo<InterestTagDto>(_mapper.ConfigurationProvider)
        .AsNoTracking(), 1, 20);
    }

在线

var count = await source.CountAsync();

of PagedList<>, 我得到一个错误

The provider for the source 'IQueryable' doesn't implement 'IAsyncQueryProvider'. Only providers that implement 'IAsyncQueryProvider' can be used for Entity Framework asynchronous operations

我认为问题是在它映射到 DTO 之后它的类型是一个 HashSet:

{System.Collections.Generic.HashSet`1[API.Entities.InterestTag].Select(dtoInterestTag => new InterestTagDto() {Id = dtoInterestTag.Id, Name = dtoInterestTag.Name})}

我有一个类似的函数可以工作,它获取 AppUsers 的列表并将其映射到 MemberDto 并使用相同的 PagedList class.

映射后类型为:

{Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable<API.DTOs.MemberDto>}

非常感谢任何帮助。谢谢!

问题出在你的“查询”(实际上不是查询,而是具体化的查询结果):

var query = (await _context.Users.Include(u => u.InterestTags).SingleOrDefaultAsync(u => u.UserName == username)).InterestTags.AsQueryable();

您正在使用 SingleOrDefaultAsync 实现您的查询,因此 EF 将调用数据库并使用 .NET 类 和集合将数据映射到内存,使 AppUser.InterestTags 成为一些集合并构建集合通常不实施 IAsyncQueryProvider,这会导致您遇到错误。

有几种可能的解决方法:

  1. 创建 PagedList.CreateAsync 重载,它接受 IEnumerable 并将您的查询结果作为普通可枚举的、不可查询的传递给它。

  2. 重写查询,使其实际上是一个查询。像这样:

var query = await _context.Users
     .Where(u => u.UserName == username)) // assuming UserName is unique
     .SelectMany(t => t.InterestTags);
// pass to CreateAsync ...

P.S.

通常也尽量防止过度获取(您只需要 InterestTags 但从数据库中获取来自 User table 的其余用户数据,尽管它不会导致提供的错误)。