Mapster icon indicating copy to clipboard operation
Mapster copied to clipboard

Make `MapsterQueryableProvider` work on non-enumerable result

Open Josephfilbert opened this issue 3 years ago • 1 comments

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.

Josephfilbert avatar May 16 '22 08:05 Josephfilbert

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.

Josephfilbert avatar Jun 03 '22 13:06 Josephfilbert

I have the same issue when getting a Count after ProjectToType

Cyrus-Sushiant avatar Dec 16 '22 21:12 Cyrus-Sushiant