Make `MapsterQueryableProvider` work on non-enumerable result
When using Entity Framework Core, I'd like to use my Extension method to return list with paging result in single call.
For example
var query = _context.Set<Student>().Where(s => s.Age > 15);
return await _mapper.From(query)
.ProjectToType<StudentDto>()
.ToPagedResultAsync(paging, cancellationToken);
where ToPagedResultAsync is
public static async Task<PagedResultModel<T>> ToPagedResultAsync<T>(this IQueryable<T> source, PagingInfoModel paging, CancellationToken cancellationToken = default)
{
var query = source.Skip(paging.Skip).Take(paging.Take);
var result = await query.ToListAsync(cancellationToken);
int count = await source.CountAsync(cancellationToken); // this throws exception
return new PagedResultModel<T>(result, new PagingResultInformation(paging, count));
}
public record PagingInfoModel(int PageNumber = 1, int PageSize = 20)
{
public int Take => PageSize;
public int Skip => (PageNumber - 1) * PageSize;
}
public record PagedResultModel<T>(IEnumerable<T> Data, PagingResultInformation Paging)
{
}
public readonly record struct PagingResultInformation(int PageNumber, int PageSize, long TotalCount)
{
public PagingResultInformation(PagingInfoModel pagingInfoModel, long totalCount) :
this(pagingInfoModel.PageNumber, pagingInfoModel.PageSize, totalCount)
{
}
}
However this results in exception when calling .CountAsync() from MapsterQueryableProvider on ExecuteAsync<TResult> method.
Because TResult is already int, not an IAsyncEnumerable instance. So that method trying to findMapsterAsyncEnumerable with first argument is int which is not there.
https://github.com/MapsterMapper/Mapster/blob/15380d077b3ffc53cf46020efa52ae44e6928289/src/Mapster.EFCore/MapsterQueryable.cs#L87
The fix is by checking the execute result from original Query Provider, if the result doesn't implement IAsyncEnumerable then just return the result.
Turns out this issue is worse. It's not about nice to have. It also makes returning non-enumerable of DTO not working.
For example:
return _context.Set<Student>().Where(s => s.Id == 1).ProjectToType<StudentDto>()
.SingleOrDefaultAsync(cancellationToken);
Will also throws the same exception as above, since the source of problem is also same.
I have the same issue when getting a Count after ProjectToType