IQueryable projections
Mapperly does support IQueryable<T>
projections:
- Mapper definition
- Usage
[Mapper]
public static partial class CarMapper
{
public static partial IQueryable<CarDto> ProjectToDto(this IQueryable<Car> q);
}
var dtos = await DbContext.Cars
.Where(...)
.ProjectToDto()
.ToListAsync();
This is useful in combination with Entity Framework and other ORM solutions which expose IQueryable<T>
.
Only fields present in the target class will be retrieved from the database.
Since queryable projection mappings use System.Linq.Expressions.Expression<T>
under the hood,
such mappings have several limitations:
- Object factories are not applied
- Constructors with unmatched optional parameters are ignored
ThrowOnPropertyMappingNullMismatch
is ignoredAllowNullPropertyAssignment
is ignored- Enum mappings do not support the
ByName
strategy - Reference handling is not supported
- Nullable reference types are disabled
Property configurations
To configure property mappings add partial mapping method definitions with attributes as needed. Set these methods to private to hide them from callers.
[Mapper]
public static partial class CarMapper
{
public static partial IQueryable<CarDto> ProjectToDto(this IQueryable<Car> q);
[MapProperty(nameof(Car.Manufacturer), nameof(CarDto.Producer)]
private static partial CarDto Map(Car car);
}
User-implemented mapping methods
Mapperly tries to inline user-implemented mapping methods. For this to work, user-implemented mapping methods need to satisfy certain limitations:
- Only expression-bodied methods or methods that consist of a single local variable declaration expression can be inlined.
- The body needs to follow the expression tree limitations.
- Nested MethodGroups cannot be inlined.
[Mapper]
public static partial class CarMapper
{
public static partial IQueryable<CarDto> ProjectToDto(this IQueryable<Car> q);
private static string MapCarBrandName(CarBrand brand)
=> band.Name;
}
If Mapperly is unable to inline a user-implemented method, RMG068
is reported.
Non-inlined method invocations can lead to more data being loaded than necessary.
Ordering and aggregating collections can also be implemented with user-implemented mapping methods:
[Mapper]
public static partial class CarMapper
{
public static partial IQueryable<CarDto> ProjectToDto(this IQueryable<Car> q);
private static partial CarModelDto MapCarModel(CarModel model);
private static ICollection<CarModelDto> MapOrderedCarBrandName(ICollection<CarModel> models)
// note: do not use the method group '.Select(MapCarModel)', as that cannot be inlined
=> models.OrderBy(x => x.Name).Select(x => MapCarModel(x)).ToList();
}
It is important that the types in the user-implemented mapping method match the types of the objects to be mapped exactly. Otherwise, Mapperly cannot resolve the user-implemented mapping methods.