Skip to main content

IQueryable projections

Mapperly does support IQueryable<T> projections:

[Mapper]
public static partial class CarMapper
{
public static partial IQueryable<CarDto> ProjectToDto(this IQueryable<Car> q);
}

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.

info

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 ignored
  • AllowNullPropertyAssignment 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 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.