Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Unflattening not working even when providing MapProperty #381

Closed
RHaughton opened this issue Apr 26, 2023 · 9 comments
Closed

[BUG] Unflattening not working even when providing MapProperty #381

RHaughton opened this issue Apr 26, 2023 · 9 comments

Comments

@RHaughton
Copy link

RHaughton commented Apr 26, 2023

I Cannot Map Nested Object with unflattening even when I specify the property with the MapPropertyAttribute

My Source Class:

public class Source
{
    public required Guid Id { get; set; }
    public required string Name { get; set; }

    public required  string SourceTimeserieId { get; set; } = string.Empty;
    public required TimeserieProvider? SourceTimeserieProvider { get; set; }
}

My Target Class:

public class Target
{
    public required Guid Id { get; init; } // this is not mapped as expected
    public required string Name { get; set; } // this is mapped as expected

    public required SourceTimeserie SourceTimeserie { get; set; } // this is not mapped as not expected
}

I would like to Map SourceTimeserie.Id property of the target with SourceTimeserieId Property from the source

SourceTimeserie class:

public class SourceTimeserie
{
    public required string Id { get; set; } = string.Empty;
    public required TimeserieProvider? Provider { get; set; }
}

When I do the inverse Mapping it works perfectly
so this
private static partial void Update(Target target, Source source);
Result:

private static partial void Update(Target target, Source source)
        {
            source.Id = target.Id;
            source.Name = target.Name;
            source.SourceTimeserieId = target.SourceTimeserie.Id; // Mapped using flattening as expected
            source.SourceTimeserieProvider = target.SourceTimeserie.Provider; // Mapped using flattening as expected
        }

On the other side:
private static partial void Update(Source source, Target target);
Doesn't map Target.SourceTimeserie.Id
even when I speficy
[MapProperty("Source.SourceCompositionId", "Target.SourceComposition.Id")]
or
[MapProperty(nameof(Source.SourceCompositionId), nameof(Target.SourceComposition.Id))]

Any Idea why the mapping doesn't work?

@RHaughton
Copy link
Author

I'm using the PreRelease version 2.8.0-next.2

@latonz
Copy link
Contributor

latonz commented Apr 26, 2023

I think the configurations you set are not correct.
Give it a try with [MapProperty("SourceCompositionId", "SourceComposition.Id")].

@RHaughton
Copy link
Author

This works. Thanks.
Is this the only way? I'm sad having those magic strings

@latonz
Copy link
Contributor

latonz commented Apr 26, 2023

The same does also work with nameof, you can even use the array ctor of the attribute.

@RHaughton
Copy link
Author

It's great that the reverse works out of the box
public static partial void Update(Target target, Source source);

Wouldn't it be possible to make a ReverseMapping ?
Like for example the .TwoWays() Mapping of Mapster or the ReverseMap() of Automapper

@latonz
Copy link
Contributor

latonz commented Apr 26, 2023

Each mapping in Mapperly has it‘s own configuration and os not directly connected to other mapping methods. As Mapperly only generates implementations of user defined partial methods, I can‘t think of an approach which could work.
Also figuring it out automatically is kinda hard, since Mapperly loops through the target properties and tries to find a matching source property for each target property. It makes use of the pascal case notation to find candidates.

@RHaughton
Copy link
Author

RHaughton commented Apr 26, 2023

So "Unflattening" out of the box is out of question?
Thanks alot for your answers and your time.

@latonz
Copy link
Contributor

latonz commented Apr 27, 2023

I think so, unless you have a good idea on how this could be solved.

@TimothyMakkison
Copy link
Collaborator

TimothyMakkison commented Apr 27, 2023

Wouldn't it be possible to make a ReverseMapping ?
Like for example the .TwoWays() Mapping of Mapster or the ReverseMap() of Automapper

It could be its own attribute that would generate a similar reversed method. It could try to reverse the mapping stragedy and reverse it, although I don't think there is curretly a way of getting this. Perhaps an analyzer could tell the user to create a reverse map for any user generated mapping functions. It would definetely be a lot of work 😆

[TwoWayMapper]
public static partial void Update(Source source, Target target);

// Generates...
public static partial void Update(Target source, Source target);

Each mapping in Mapperly has it‘s own configuration and os not directly connected to other mapping methods. As Mapperly only generates implementations of user defined partial methods, I can‘t think of an approach which could work.
Also figuring it out automatically is kinda hard, since Mapperly loops through the target properties and tries to find a matching source property for each target property. It makes use of the pascal case notation to find candidates.

I ran into a similar issue when looking at #349 and #103. Mapperly descends down the mapping tree, generating the mappings but does not pass information up to its parent mapping. This way you can't pass parameters down to complex mappings or user mappers that take additional parameters. Mapperly would have to be refactored to depth first descend and work its way back up so that the correct parameters can be passed down.

Examples where it could be smarter:

 // This is the same as [MapProperty(nameof(Car.Manufacturer), nameof(CarDto.Producer))] but Mapperly requires a parameterless constructor with accessible parameters here.
[MapProperty("Manufacturer.Id", "Producer.Id")]
[MapProperty("Manufacturer.Name", "Producer.Name")]
public static partial CarDto MapCarToDto(Car car); // ProducerDto has no accessible parameterless constructor.

Ideally it should generate

// this would be useful for #109. Not possible mapperly 
target.Producer = MapToProducerDto(car.Manufacturer.Id, car.Manufacturer.Name);

// smart mapper 
target.Producer = MapToProducerDto(car.Manufacturer);

Second Example:

[MapProperty("Manufacturer.Id", "Producer.Id")]
[MapProperty(nameof(Car.NumberOfSeats), "Producer.Name")]
public static partial CarDto MapCarToDto(Car car);  // ProducerDto has no accessible parameterless constructor.

Ideally it should generate

target.Producer = MapToProducerDto(car.NumberOfSeats, car.Manufacturer.Name);

// smart mapper
target.Producer = MapToProducerDto(car);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants