C
C#2y ago
FusedQyou

❔ Unable to get automapper's projectTo() to work properly.

I need to use ProjectTo() to properly cast a query into a DTO, but in this case my user can only contains pictures which have Deleted = false. The code below works, but all photo's are returned, including deleted ones. The commented Include() works fine, but I need the mapping to happen on query level, and not after, because of the creation of a paginatedList. If I map later, the PaginatedList misses crucial information, and I do not want to change its behaviour.
public async Task<PaginatedList<DTODataUser>> GetAllDTOPaginatedAsync(PaginatedQuery pagination, bool includeDeleted, CancellationToken cancellationToken)
{
var userQuery = this._databaseContext
.Users
.Where(x => !includeDeleted ? !x.Deleted : true)
.ProjectTo<DTODataUser>(this._mapper.ConfigurationProvider, null, (user) => user.Photos.Where(y => !includeDeleted ? !y.Deleted : true));
//.Include(x => x.Photos.Where(y => !includeDeleted ? !y.Deleted : true))

var users = await PaginatedList<DTODataUser>.CreateAsync(userQuery, pagination, cancellationToken);
return users;
}
public async Task<PaginatedList<DTODataUser>> GetAllDTOPaginatedAsync(PaginatedQuery pagination, bool includeDeleted, CancellationToken cancellationToken)
{
var userQuery = this._databaseContext
.Users
.Where(x => !includeDeleted ? !x.Deleted : true)
.ProjectTo<DTODataUser>(this._mapper.ConfigurationProvider, null, (user) => user.Photos.Where(y => !includeDeleted ? !y.Deleted : true));
//.Include(x => x.Photos.Where(y => !includeDeleted ? !y.Deleted : true))

var users = await PaginatedList<DTODataUser>.CreateAsync(userQuery, pagination, cancellationToken);
return users;
}
How can I replicate Include to work with projectTo? As said above, the current version is allowed, but deleted photo's are returned too.
12 Replies
Angius
Angius2y ago
You do it in automapper's config
FusedQyou
FusedQyou2y ago
I just added .ExplicitExpansion(), but this did not change its behaviour
FusedQyou
FusedQyou2y ago
FusedQyou
FusedQyou2y ago
The conversion is DataUser -> DTODataUser Am I missing something?
Angius
Angius2y ago
Huh Honestly, that's why I dropped AutoMapper, way too much fuss But I think that you can have a .Where() instead of that .ExplicitExpansion()..?
FusedQyou
FusedQyou2y ago
Yeah, but then I need a way to determine if the includeDeleted boolean is set
Angius
Angius2y ago
You can pass that to the mapper config The way to do it is quite ugly, but there is a way Again, one of the reasons I dropped it lol
FusedQyou
FusedQyou2y ago
Do/Can can you suggest an alternative?
Angius
Angius2y ago
Just a .Select() If you need to reuse some mapping, make a class with static Expression<Func<TSource, TTarget>> properties and use those
FusedQyou
FusedQyou2y ago
Makes sense After all it's just a lazy way of using it But the reason why I need this is because PagedList inherits from List, and any properties in there are not serialized properly, and neither are they converted if I map later. I can also just fix this by not making it inherit from List I do kinda want ProjectTo to just work properly because otherwise I get too much data, but oh well I suppose I can still use a mapper outside of this service
Angius
Angius2y ago
static class UserMappings
{
public static Expression<Func<User, UserDto>> ToDto = user => new UserDto {
Name = user.UserName,
Age = user.Age,
Comments = user.Comments.Where(c => !c.IsDeleted)
}


public static Expression<Func<User, UserDto>> ToDtoWithParam(int param) => user => new UserDto {
Name = user.UserName,
Age = user.Age,
Whatever = param > 10 ? user.Foo : user.Bae
}
}
static class UserMappings
{
public static Expression<Func<User, UserDto>> ToDto = user => new UserDto {
Name = user.UserName,
Age = user.Age,
Comments = user.Comments.Where(c => !c.IsDeleted)
}


public static Expression<Func<User, UserDto>> ToDtoWithParam(int param) => user => new UserDto {
Name = user.UserName,
Age = user.Age,
Whatever = param > 10 ? user.Foo : user.Bae
}
}
An example of doing the mappings with Expression<Func<,>> Then just .Select(UserMappings.ToDto) Not sure how that paginated list of yours works, so can't speak on that Me, I just have a simple extension method for IQueryable
public static IQueryable<T> Paginate<T>(this IQueryable<T> query, int page, int perPage)
{
if (page < 1)
throw new ArgumentOutOfRangeException(nameof(page), "Page has to be greater than 0");
if (perPage < 1)
throw new ArgumentOutOfRangeException(nameof(perPage), "PerPage has to be greater than 0");

return query
.Skip(Math.Max(0, page - 1) * perPage)
.Take(perPage);
}
public static IQueryable<T> Paginate<T>(this IQueryable<T> query, int page, int perPage)
{
if (page < 1)
throw new ArgumentOutOfRangeException(nameof(page), "Page has to be greater than 0");
if (perPage < 1)
throw new ArgumentOutOfRangeException(nameof(perPage), "PerPage has to be greater than 0");

return query
.Skip(Math.Max(0, page - 1) * perPage)
.Take(perPage);
}
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.