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

Reverse mappings #1250

Open
InspiringCode opened this issue Apr 24, 2024 · 6 comments
Open

Reverse mappings #1250

InspiringCode opened this issue Apr 24, 2024 · 6 comments
Labels
enhancement New feature or request

Comments

@InspiringCode
Copy link

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.

@InspiringCode InspiringCode added the enhancement New feature or request label Apr 24, 2024
@latonz
Copy link
Contributor

latonz commented Apr 25, 2024

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.

@InspiringCode
Copy link
Author

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 Discount property, an unflattening is automatically performance inverse to the original MapProperty.
  • Renames done via MapProperty are reversed.
  • Mappings can be manually overwritten.

For MapProperty attributes that have set a custom converter, I see to possible API variants:

  1. Specify MapProperty on each reverse mapping as in the example above.
  2. 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?

@latonz latonz changed the title Support for reverse mappings? Reverse mappings Apr 29, 2024
@latonz
Copy link
Contributor

latonz commented May 16, 2024

Rel. #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).

@InspiringCode
Copy link
Author

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 MapProperty attribute with a new configuration (overwrite the existing one).

Are there any plans to implement reverse mappings?

@latonz
Copy link
Contributor

latonz commented Jun 5, 2024

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!

@latonz
Copy link
Contributor

latonz commented Jun 5, 2024

Instead of a new attribute this could work by implementing #513 IncludeMappingConfiguration with an additional property Reverse, [IncludeMappingConfiguration(nameof(ToDTO), Reverse = true)].

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

No branches or pull requests

2 participants