mapperly icon indicating copy to clipboard operation
mapperly copied to clipboard

Projection with UseStaticMappers calls the mapping function instead of using inline generator

Open ghost opened this issue 7 months ago • 1 comments

Please do the checklist before filing an issue:

  • [x] I have read the documentation, including the FAQ
  • [x] I can reproduce the bug using the latest prerelease version
  • [x] I have searched existing discussion and issue to avoid duplicates

Describe the bug UseStaticMapper correctly finds the correct mapping functions for the dto, how ever when it comes to projection, it calls the same mapping function instead of creating the projection query.

you get the correct projection when you copy the mapping locally.

Is this what is outlined as one of the limitations with "Deep Cloning"? or a limitation in general?

Repro A link to a repro Gist or GitHub repository or a link to a Mapperly fork with a failing test.

Declaration code

public class Maker
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Car
{
    public int Id { get; set; }
    public string Name { get; set; } = null!;
    public Maker Make { get; set; } = null!;
}

public record MakerDto
{
    public int Id { get; init; }
    public string MakerName { get; init; } = null!;
}

public record CarDto
{
    public int Id { get; init; }
    public string CarName { get; init; } = null!;
    public MakerDto Maker { get; init; } = null!;
}

[Mapper]
public static partial class MakerMapper
{
    public static partial IQueryable<MakerDto> ProjectToMakerDto(this IQueryable<Maker> query);
    
    [MapperRequiredMapping(RequiredMappingStrategy.Target)]
    [MapProperty(nameof(Maker.Name), nameof(MakerDto.MakerName))]
    public static partial MakerDto ToMakerDto(this Maker maker);
}

[Mapper]
[UseStaticMapper(typeof(MakerMapper))]
public static partial class CarMapper
{
    public static partial IQueryable<CarDto> ProjectToMakerDto(this IQueryable<Car> query);
    
    [MapperRequiredMapping(RequiredMappingStrategy.Target)]
    [MapProperty(nameof(Car.Name), nameof(CarDto.CarName))]
    [MapProperty(nameof(Car.Make), nameof(CarDto.Maker))]
    public static partial CarDto MapToCarDto(this Car car);
    

Actual relevant generated code

[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.2.1.0")]
        public static partial global::System.Linq.IQueryable<global::TestingNamespace.CarDto> ProjectToMakerDto(this global::System.Linq.IQueryable<global::TestingNamespace.Car> query)
        {
#nullable disable
            return global::System.Linq.Queryable.Select(
                query,
                x => new global::TestingNamespace.CarDto()
                {
                    Id = x.Id,
                    CarName = x.Name,
                    Maker = global::TestingNamespace.MakerMapper.ToMakerDto(x.Make),
                }
            );
#nullable enable
        }

Expected relevant generated code

[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.2.1.0")]
        public static partial global::System.Linq.IQueryable<global::TestingNamespace.CarDto> ProjectToMakerDto(this global::System.Linq.IQueryable<global::TestingNamespace.Car> query)
        {
#nullable disable
            return global::System.Linq.Queryable.Select(
                query,
                x => new global::TestingNamespace.CarDto()
                {
                    Id = x.Id,
                    CarName = x.Name,
                    Maker = new global::TestingNamespace.MakerDto()
                    {
                        Id = x.Make.Id,
                        MakerName = x.Make.Name,
                    },
                }
            );
#nullable enable
        }

Reported relevant diagnostics

Environment (please complete the following information):

  • Mapperly Version: [4.2.1]
  • Nullable reference types: [enabled]
  • .NET Version: [e.g. .NET 9.0.4]
  • Target Framework: [net9.0]
  • Compiler Version: [4.13.0-3.25210.5 (fe188fa9)]
  • C# Language Version: [13.0]
  • IDE: [e.g. Visual Studio v17.5.2 or Rider 2024.3.7]
  • OS: [e.g. Windows 10]

Additional context Not sure if this is a limitation / feature or a bug. wont claim to know enough about the mapperly source code or code generation to make that judgement.

for now, my work around is to copy the map into the local mapping class.

ghost avatar May 22 '25 04:05 ghost

Any updates on this? the slightly better workaround I found was to copy just the signature, and reuse the mapping configuration using IncludeMappingConfiguration

[Mapper]
public static partial class FooMapper
{
    public static partial IQueryable<Target> ProjectToDto(this IQueryable<Source> q);
    
    [IncludeMappingConfiguration(nameof(@ChildMapper.MapToTarget))]
    private static partial TargetChild MapToTarget(SourceChild car);
}

[Mapper]
public static partial class ChildMapper
{
    [MapProperty(nameof(SourceChild.FullName), nameof(TargetChild.Name))]
    public static partial TargetChild MapToTarget(SourceChild car);
}

in this way, i have only one centralized point to change if I ever need to change the mapping of that child property

Levyks avatar Nov 14 '25 16:11 Levyks