Add support for destination value in user implemented mapping methods
Is your feature request related to a problem? Please describe.
Currently, the User implemented mapping methods feature in Mapperly only allows users to provide a method body that maps a single source type to a single destination type. However, in some scenarios, it may be necessary to have more control over the mapping process, such as when mapping optional values or when the user wants to choose which value (source or destination) to use for the destination property.
Describe the solution you'd like
I propose adding support for destination values in the User implemented mapping methods feature. This would allow users to specify which value (source or destination) to use for the destination property when mapping between objects.
For example, the following code snippet demonstrates how this feature might be used to map an optional value:
MyMapper.cs
[Mapper]
public partial class MyMapper
{
public partial void MapPersonToPersonDto(Person source, PersonDto destination) { }
private static int? FromOptional<T>(Optional<int?> source, int? destination) => source.HasValue ? source.Value : destination;
}
MyMapper.g.cs
[Mapper]
public partial class MyMapper
{
public partial void MapPersonToPersonDto(Person source, PersonDto destination)
{
destination.Id = source.Id;
/* other mappings omitted for brevity */
destination.Age = FromOptional(source.Age, destination.Age);
}
}
With this change, users would have more control over how mappings are defined and could handle more complex mapping scenarios that are not possible with the current implementation.
I think your use case could be implemented by using the after map feature with an ignored property:
[Mapper]
public partial class MyMapper
{
public void MapPersonToPersonDto(Person person, PersonDto destination)
{
MapPersonToPersonDtoInternal(person, destination);
destination.Age = CustomAgeMapping(person, destination);
}
[MapperIgnoreTarget(nameof(PersonDto.Age))]
private partial void MapPersonToPersonDtoInternal(Person person, PersonDto destination)
}
Would this work for your use case?
It would probably work but that would mean that the mapping was not generated for me and I would have to do that manually? Then the point of a mapper is kinda mute?
Using the after map feature would not work in the case where PersonDto.Age is required and when using a non "void mapping method" (when not mapping to existing object instance)
[Mapper]
public partial class MyMapper
{
public PersonDto MapPersonToPersonDto(Person person)
{
var personDto = MapPersonToPersonDtoInternal(person);
destination.Age = CustomAgeMapping(person, destination);
}
[MapperIgnoreTarget(nameof(PersonDto.Age))]
private partial PersonDto MapPersonToPersonDtoInternal(Person person) // ~ ERROR: Required property Age on mapping target type was not found on the mapping source type Person
}
Maybe fixing #103 would also cover this?
I have a source UpsertModel which doesn't contain the Id or the createdTimestamp, but in my target model they are required constructor parameters. In the target model they are not publicly settable.
I'd like to be able to configure the mapper to pass these along somehow. These could be additional params to the mapping function, or decorated attributes, or something else.
What I don't want to do is modify my target model. For now though I'm forced to add a SetId(...) and SetCreatedTimestamp(...) method to the target model class. Sad.