Reverse mappings
Are there any plans to support reverse mappings like they are supported in AutoMapper?
In some of our projects we use this functionality quite extensively but we want to slowly migrate to Mapperly and don't want to duplicate all our mappings for both directions.
I would really love to hear your plans/opinions about this.
How would you like to define such a reverse mapping in your mapper definition? Currently Mapperly only ever provides the implementation to user implemented methods.
Here is my first idea about a possible API with a (bit contrived) example:
Let's assume we have the following classes:
public class ProductDTO {
public Guid Id { get; set; }
public string Name { get; set; }
public decimal PriceInEuro { get; set; }
public decimal Discount { get; set ;}
public string Category { get; set; }
}
public class Product {
public Guid Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public decimal DiscountScheme Discounts { get; } = new();
public ProductCategory Category { get; set; }
public User CreatedBy { get; set; }
}
public class UpdateDiscount {
public Guid Id { get; set; }
public DiscountScheme Discounts { get; } = new ();
}
Given the following mapper:
public static partial class Mapper {
[MapProperty(nameof(Product.Price), nameof(ProductDTO.PriceInEuro))]
[MapProperty(nameof(@Product.Discounts.EndUsers), nameof(Discount))]
[MapProperty(nameof(Product.Category), nameof(ProductDTO.Category), Use=nameof(CategoryToString))]
public static partial ProductDTO ToDTO(this Product source);
[MapReverse(nameof(ToDTO))]
[MapProperty(nameof(ProductDTO.Categor), nameof(Product.Category), Use=nameof(StringToCategory))]
public static partial void ToProduct(this ProductDTO source, Product target);
[MapReverse(nameof(ToDTO))]
public static partial void ToUpdateDiscount(this ProductDTO source, UpdateDiscount target);
}
Then we could perform the following mappings:
ProductDTO dto = new() { Name = "test", PriceInEuro = 42, Discount = 15, Category = "Tech" };
Product p = new Product(); // TODO: load actual product here
dto.ToProduct(p);
p.Name.Should().Be("test");
p.Price.Should().Be(42);
p.Category.Should().Be(new ProductCategory("Tech"));
p.DiscountSchme.EndUsers.Should().Be(15);
UpdateDiscount u = new(); // TODO: It would make sense, to preinit the DiscountScheme here
dto.ToUpdateProduct(u);
u.Id.Should().Be(dto.Id);
u.DiscountScheme.EndUsers.Should().Be(15);
Remarks:
- For the
Discountproperty, an unflattening is automatically performance inverse to the originalMapProperty. - Renames done via
MapPropertyare reversed. - Mappings can be manually overwritten.
For MapProperty attributes that have set a custom converter, I see to possible API variants:
- Specify
MapPropertyon each reverse mapping as in the example above. - Specify by the inverse converter in the original mapping. E.g. `[MapProperty(..., Use = nameof(CategoryToString), UseReverse=nameof(StringToCategory)]
The second approach would have the advantage, that I don't have to duplicate the inverse mapping (StringToCategory) on each reverse map that includes the Category property. On the downside it adds one additional property to the API.
I think this would be a big step to closing the gap between Mapperly and AutoMapper. When I first presented Mapperly within my company, the very first question asked by colleagues was, whether Mapperly supports inverse mappings or not. They said that they use this feature a lot with AutoMapper.
What do you think?
Rel. https://github.com/riok/mapperly/issues/513 Can you elaborate on
Mappings can be manually overwritten.
How would that work? Based on what it is decided whether this is an additional configuration or should overwrite an existing one?
I'm not a fan of UseReverse as this would bloat the API (for every configuration a *Reverse would be needed.
We could probably just assume the same configuration as the original MapProperty had. If this does not work (which probably would often be the case as the Use methods only work one way etc.), the user needs to specify an explicit MapProperty attribute with a new configuration (overwrite the existing one).
Mappings can be manually overwritten.
By that I meant exactly what you described in your answer:
If this does not work (which probably would often be the case as the Use methods only work one way etc.), the user needs to specify an explicit
MapPropertyattribute with a new configuration (overwrite the existing one).
Are there any plans to implement reverse mappings?
I like the idea of supporting this, but our resources are limited and this is feature is not a top priority at the moment. However, we are happy to accept community PR's though!
Instead of a new attribute this could work by implementing https://github.com/riok/mapperly/issues/513 IncludeMappingConfiguration with an additional property Reverse, [IncludeMappingConfiguration(nameof(ToDTO), Reverse = true)].