diff --git a/docs/en/framework/architecture/domain-driven-design/application-services.md b/docs/en/framework/architecture/domain-driven-design/application-services.md index 4d32f853dd..968ceb434d 100644 --- a/docs/en/framework/architecture/domain-driven-design/application-services.md +++ b/docs/en/framework/architecture/domain-driven-design/application-services.md @@ -134,7 +134,7 @@ The `CreateAsync` method above manually creates a `Book` entity from given `Crea However, in many cases, it's very practical to use **auto object mapping** to set properties of an object from a similar object. ABP provides an [object to object mapping](../../infrastructure/object-to-object-mapping.md) infrastructure to make this even easier. -Object to object mapping provides abstractions and it is implemented by the [AutoMapper](https://automapper.org/) library by default. +Object to object mapping provides abstractions and it is implemented by the [Mapperly](https://mapperly.riok.app/) library by default. Let's create another method to get a book. First, define the method in the `IBookAppService` interface: @@ -162,36 +162,32 @@ public class BookDto } ```` -AutoMapper requires to create a mapping [profile class](https://docs.automapper.org/en/stable/Configuration.html#profile-instances). Example: +[Mapperly](https://mapperly.riok.app/) requires to create a mapping class that implements the `MapperBase` class with the `[Mapper]` attribute as follows: -````csharp -public class MyProfile : Profile +```csharp +[Mapper] +public partial class BookToBookDtoMapper : MapperBase { - public MyProfile() - { - CreateMap(); - } + public override partial BookDto Map(Book source); + + public override partial void Map(Book source, BookDto destination); } -```` +``` -You should then register profiles using the `AbpAutoMapperOptions`: +Then, if your application uses multiple mapping providers, you should add the following configuration to your module's `ConfigureServices` method to decide which mapping provider to use: ````csharp -[DependsOn(typeof(AbpAutoMapperModule))] +[DependsOn(typeof(AbpMapperlyModule))] public class MyModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { - Configure(options => - { - //Add all mappings defined in the assembly of the MyModule class - options.AddMaps(); - }); + context.Services.AddMapperlyObjectMapper(); } } ```` -`AddMaps` registers all profile classes defined in the assembly of the given class, typically your module class. It also registers for the [attribute mapping](https://docs.automapper.org/en/stable/Attribute-mapping.html). +With this configuration, your module will use [Mapperly](https://mapperly.riok.app/) as the default mapping provider and you don't need to register mapping classes manually. Then you can implement the `GetAsync` method as shown below: @@ -291,16 +287,21 @@ public class CreateUpdateBookDto } ```` -[Profile](https://docs.automapper.org/en/stable/Configuration.html#profile-instances) class of DTO class. +Define the mapping classes for [Mapperly](https://mapperly.riok.app/) as follows: ```csharp -public class MyProfile : Profile +[Mapper] +public partial class BookToBookDtoMapper : MapperBase { - public MyProfile() - { - CreateMap(); - CreateMap(); - } + public override partial BookDto Map(Book source); + public override partial void Map(Book source, BookDto destination); +} + +[Mapper] +public partial class CreateUpdateBookDtoToBookMapper : MapperBase +{ + public override partial Book Map(CreateUpdateBookDto source); + public override partial void Map(CreateUpdateBookDto source, Book destination); } ``` diff --git a/docs/en/framework/infrastructure/entity-cache.md b/docs/en/framework/infrastructure/entity-cache.md index 64a03ad4c4..cba0cf8eaa 100644 --- a/docs/en/framework/infrastructure/entity-cache.md +++ b/docs/en/framework/infrastructure/entity-cache.md @@ -88,6 +88,17 @@ public class MyMapperProfile : Profile } ``` +If you are using [Mapperly](https://mapperly.riok.app/), you can create a new mapping class that implements the `MapperBase` class with the `[Mapper]` attribute as follows: + +```csharp +[Mapper] +public partial class ProductToProductDtoMapper : MapperBase +{ + public override partial ProductDto Map(Product source); + public override partial void Map(Product source, ProductDto destination); +} +``` + Now, you can inject the `IEntityCache` service wherever you want: ```csharp diff --git a/docs/en/modules/docs.md b/docs/en/modules/docs.md index b564980ba3..b9c3209e3f 100644 --- a/docs/en/modules/docs.md +++ b/docs/en/modules/docs.md @@ -148,11 +148,6 @@ An ABP module must declare `[DependsOn]` attribute if it has a dependency upon a { options.DefinitionProviders.Add(); }); - - Configure(options => - { - options.AddProfile(); - }); } } ``` diff --git a/docs/en/others/why-abp-platform.md b/docs/en/others/why-abp-platform.md index c13631546b..61756d2654 100644 --- a/docs/en/others/why-abp-platform.md +++ b/docs/en/others/why-abp-platform.md @@ -169,7 +169,7 @@ The learning curve is much lower than not using the ABP. That may sound surprisi ABP creates a full stack, production-ready, working solution for you in seconds. Many of the real-life problems are already solved and many fine tune configurations are already applied for the ASP.NET Core and the other used libraries. If you start from scratch, you will experience and learn all these details yourself to truly implement your solution. -ABP uses the industry standard frameworks, libraries and systems you already know (or need to learn to build a real-world product) like Angular, Blazor, MAUI, EF Core, AutoMapper, OpenIddict, Bootstrap, Redis, SignalR... etc. So, all your knowledge is directly re-usable with the ABP. ABP even simplifies using these libraries and systems and solves the integration problems. If you don't know these tools now, learning them will be easier within the ABP. +ABP uses the industry standard frameworks, libraries and systems you already know (or need to learn to build a real-world product) like Angular, Blazor, MAUI, EF Core, AutoMapper (switched to Mapperly due to licensing concerns), OpenIddict, Bootstrap, Redis, SignalR... etc. So, all your knowledge is directly re-usable with the ABP. ABP even simplifies using these libraries and systems and solves the integration problems. If you don't know these tools now, learning them will be easier within the ABP. ABP provides an excellent infrastructure to apply DDD principles and other best practices. It provides a lot of sweet abstractions and automation to reduce the repeating code. However, it doesn't force you to use or apply all these. A common mistake is to see that ABP has a lot of features, and it is hard to learn all of them. Having a lot of features is an advantage when you come to the point that you need them. However, you don't need to know a feature until you need it, and you can continue with the development approach you are used to. You can still write code as you are used to as if ABP doesn't provide all these benefits. Learning the ABP infrastructure is progressive. You will love it whenever you learn a new feature but can continue developing without knowing its existence. diff --git a/docs/en/release-info/migration-guides/pro/openiddict-microservice.md b/docs/en/release-info/migration-guides/pro/openiddict-microservice.md index 818a8c368a..33c319111b 100644 --- a/docs/en/release-info/migration-guides/pro/openiddict-microservice.md +++ b/docs/en/release-info/migration-guides/pro/openiddict-microservice.md @@ -472,17 +472,12 @@ In `appsettings.json` replace **IdentityServer** section with **OpenIddict** and typeof(AbpOpenIddictProWebModule), ``` -- In **IdentityServiceWebModule.cs** add object mapping configurations: +- In **IdentityServiceWebModule.cs** add object mapping configurations for [Mapperly](https://mapperly.riok.app/) (if you are using an another mapping providers, see the [Object to Object Mapping](../../../framework/infrastructure/object-to-object-mapping.md) documentation): ```csharp - context.Services.AddAutoMapperObjectMapper(); - Configure(options => - { - options.AddMaps(validate: true); - }); + context.Services.AddMapperlyObjectMapper(); ``` - ### Shared Hosting Module - In **MyApplicationSharedHostingModule** replace the **database configuration**: diff --git a/docs/en/solution-templates/guide.md b/docs/en/solution-templates/guide.md index cb14f57ef3..ded0c49f20 100644 --- a/docs/en/solution-templates/guide.md +++ b/docs/en/solution-templates/guide.md @@ -27,7 +27,7 @@ Besides the overall solution structure, the internals of each project in a solut ### Library Integrations & Configurations -When you use ABP startup solution templates to create a new solution, some **fundamental library installations** ([Serilog](https://serilog.net/), [Autofac](https://autofac.org/), [AutoMapper](https://automapper.org/), [Swagger](https://swagger.io/), [HealthCheck](https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks) and others..) and their fine-tuned configurations are already prepared for you. Also, required **[ABP packages](https://abp.io/packages)** are just installed based on your preferences and configured for **development and production environments**. +When you use ABP startup solution templates to create a new solution, some **fundamental library installations** ([Serilog](https://serilog.net/), [Autofac](https://autofac.org/), [Mapperly](https://mapperly.riok.app/), [Swagger](https://swagger.io/), [HealthCheck](https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks) and others..) and their fine-tuned configurations are already prepared for you. Also, required **[ABP packages](https://abp.io/packages)** are just installed based on your preferences and configured for **development and production environments**. ### Development Ready diff --git a/docs/en/tutorials/book-store/part-01.md b/docs/en/tutorials/book-store/part-01.md index 6b856d9687..4634a68c67 100644 --- a/docs/en/tutorials/book-store/part-01.md +++ b/docs/en/tutorials/book-store/part-01.md @@ -291,22 +291,17 @@ public class BookDto : AuditedEntityDto * The `BookDto` is used to transfer the book data to the presentation layer in order to show the book information on the UI. * The `BookDto` is derived from the `AuditedEntityDto` which has audit properties just like the `Book` entity defined above. -It will be needed to map the `Book` entities to the `BookDto` objects while returning books to the presentation layer. [AutoMapper](https://automapper.org) library can automate this conversion when you define the proper mapping. The startup template comes with AutoMapper pre-configured. So, you can just define the mapping in the `BookStoreApplicationAutoMapperProfile` class in the `Acme.BookStore.Application` project: +It will be needed to map the `Book` entities to the `BookDto` objects while returning books to the presentation layer. [Mapperly](https://mapperly.riok.app/) library can automate this conversion when you define the proper mapping. The startup template comes with Mapperly pre-configured. So, you can just define the mapping in the `BookStoreApplicationMappers` class in the `Acme.BookStore.Application` project: -````csharp -using Acme.BookStore.Books; -using AutoMapper; - -namespace Acme.BookStore; - -public class BookStoreApplicationAutoMapperProfile : Profile +```csharp +[Mapper] +public partial class BookToBookDtoMapper : MapperBase { - public BookStoreApplicationAutoMapperProfile() - { - CreateMap(); - } + public override partial BookDto Map(Book source); + + public override partial void Map(Book source, BookDto destination); } -```` +``` > See the [object to object mapping](../../framework/infrastructure/object-to-object-mapping.md) document for details. @@ -343,21 +338,23 @@ public class CreateUpdateBookDto As done to the `BookDto` above, we should define the mapping from the `CreateUpdateBookDto` object to the `Book` entity. The final class will be as shown below: -````csharp -using Acme.BookStore.Books; -using AutoMapper; +```csharp +[Mapper] +public partial class BookToBookDtoMapper : MapperBase +{ + public override partial BookDto Map(Book source); -namespace Acme.BookStore; + public override partial void Map(Book source, BookDto destination); +} -public class BookStoreApplicationAutoMapperProfile : Profile +[Mapper] +public partial class CreateUpdateBookDtoToBookMapper : MapperBase { - public BookStoreApplicationAutoMapperProfile() - { - CreateMap(); - CreateMap(); - } + public override partial Book Map(CreateUpdateBookDto source); + + public override partial void Map(CreateUpdateBookDto source, Book destination); } -```` +``` ### IBookAppService @@ -416,7 +413,7 @@ public class BookAppService : * `BookAppService` is derived from `CrudAppService<...>` which implements all the CRUD (create, read, update, delete) methods defined by the `ICrudAppService`. * `BookAppService` injects `IRepository` which is the default repository for the `Book` entity. ABP automatically creates default repositories for each aggregate root (or entity). See the [repository document](../../framework/architecture/domain-driven-design/repositories.md). -* `BookAppService` uses `IObjectMapper` service ([see](../../framework/infrastructure/object-to-object-mapping.md)) to map the `Book` objects to the `BookDto` objects and `CreateUpdateBookDto` objects to the `Book` objects. The Startup template uses the [AutoMapper](http://automapper.org/) library as the object mapping provider. We have defined the mappings before, so it will work as expected. +* `BookAppService` uses `IObjectMapper` service ([see](../../framework/infrastructure/object-to-object-mapping.md)) to map the `Book` objects to the `BookDto` objects and `CreateUpdateBookDto` objects to the `Book` objects. The Startup template uses the [Mapperly](https://mapperly.riok.app/) library as the object mapping provider. We have defined the mappings before, so it will work as expected. ## Auto API Controllers diff --git a/docs/en/tutorials/book-store/part-03.md b/docs/en/tutorials/book-store/part-03.md index 45444ee76f..e67353b1fa 100644 --- a/docs/en/tutorials/book-store/part-03.md +++ b/docs/en/tutorials/book-store/part-03.md @@ -298,23 +298,17 @@ public class EditModalModel : BookStorePageModel ### Mapping from BookDto to CreateUpdateBookDto -To be able to map the `BookDto` to `CreateUpdateBookDto`, configure a new mapping. To do this, open the `BookStoreWebAutoMapperProfile.cs` file in the `Acme.BookStore.Web` project and change it as shown below: +To be able to map the `BookDto` to `CreateUpdateBookDto`, configure a new mapping. To do this, open the `BookStoreWebMappers.cs` file in the `Acme.BookStore.Web` project and change it as shown below: -````csharp -using AutoMapper; - -namespace Acme.BookStore.Web; - -public class BookStoreWebAutoMapperProfile : Profile +```csharp +[Mapper] +public partial class BookDtoToCreateUpdateBookDtoMapper : MapperBase { - public BookStoreWebAutoMapperProfile() - { - CreateMap(); - } -} -```` + public override partial CreateUpdateBookDto Map(BookDto source); -* We have just added `CreateMap();` to define this mapping. + public override partial void Map(BookDto source, CreateUpdateBookDto destination); +} +``` > Notice that we do the mapping definition in the web layer as a best practice since it is only needed in this layer. @@ -1288,28 +1282,26 @@ We can now define a modal to edit the book. Add the following code to the end of ```` -### AutoMapper Configuration +### Mapperly Configuration The base `AbpCrudPageBase` uses the [object to object mapping](../../framework/infrastructure/object-to-object-mapping.md) system to convert an incoming `BookDto` object to a `CreateUpdateBookDto` object. So, we need to define the mapping. -Open the `BookStoreBlazorAutoMapperProfile` inside the {{ if UI == "BlazorServer" }}`Acme.BookStore.Blazor` {{ else if UI == "MAUIBlazor" }}`Acme.BookStore.MauiBlazor` {{ else }}`Acme.BookStore.Blazor.Client`{{ end }} project and change the content as the following: +Open the `BookStoreBlazorMappers` inside the {{ if UI == "BlazorServer" }}`Acme.BookStore.Blazor` {{ else if UI == "MAUIBlazor" }}`Acme.BookStore.MauiBlazor` {{ else }}`Acme.BookStore.Blazor.Client`{{ end }} project and change the content as the following: -````csharp -using Acme.BookStore.Books; -using AutoMapper; +```csharp +using Riok.Mapperly.Abstractions; +using Volo.Abp.Mapperly; {{ if UI == "BlazorServer" }}namespace Acme.BookStore.Blazor; {{ else if UI == "MAUIBlazor" }}namespace Acme.BookStore.MauiBlazor; {{ else }}namespace Acme.BookStore.Blazor.Client;{{ end }} -public class BookStoreBlazorAutoMapperProfile : Profile +[Mapper] +public partial class BookDtoToCreateUpdateBookDtoMapper : MapperBase { - public BookStoreBlazorAutoMapperProfile() - { - CreateMap(); - } -} -```` + public override partial CreateUpdateBookDto Map(BookDto source); -* We've just added the `CreateMap();` line to define the mapping. + public override partial void Map(BookDto source, CreateUpdateBookDto destination); +} +``` ### Test the Editing Modal diff --git a/docs/en/tutorials/book-store/part-08.md b/docs/en/tutorials/book-store/part-08.md index a7d73f0209..ac49c6aee4 100644 --- a/docs/en/tutorials/book-store/part-08.md +++ b/docs/en/tutorials/book-store/part-08.md @@ -193,7 +193,7 @@ public async Task GetAsync(Guid id) } ```` -This method simply gets the `Author` entity by its `Id`, converts to the `AuthorDto` using the [object to object mapper](../../framework/infrastructure/object-to-object-mapping.md). This requires to configure the AutoMapper, which will be explained later. +This method simply gets the `Author` entity by its `Id`, converts to the `AuthorDto` using the [object to object mapper](../../framework/infrastructure/object-to-object-mapping.md). This requires to configure the Mapperly, which will be explained later. ### GetListAsync @@ -350,12 +350,18 @@ Finally, add the following entries to the `Localization/BookStore/en.json` insid ## Object to Object Mapping -`AuthorAppService` is using the `ObjectMapper` to convert the `Author` objects to `AuthorDto` objects. So, we need to define this mapping in the AutoMapper configuration. +`AuthorAppService` is using the `ObjectMapper` to convert the `Author` objects to `AuthorDto` objects. So, we need to define this mapping in the Mapperly configuration. -Open the `BookStoreApplicationAutoMapperProfile` class inside the `Acme.BookStore.Application` project and add the following line to the constructor: +Open the `BookStoreApplicationMappers` class inside the `Acme.BookStore.Application` project and define the following mapping class: ````csharp -CreateMap(); +[Mapper] +public partial class AuthorToAuthorDtoMapper : MapperBase +{ + public override partial AuthorDto Map(Author source); + + public override partial void Map(Author source, AuthorDto destination); +} ```` ## Data Seeder diff --git a/docs/en/tutorials/book-store/part-09.md b/docs/en/tutorials/book-store/part-09.md index 06db5f37d9..18ecd2726d 100644 --- a/docs/en/tutorials/book-store/part-09.md +++ b/docs/en/tutorials/book-store/part-09.md @@ -335,27 +335,16 @@ The main reason of this decision was to show you how to use a different model cl * Added `[DataType(DataType.Date)]` attribute to the `BirthDate` which shows a date picker on the UI for this property. * Added `[TextArea]` attribute to the `ShortBio` which shows a multi-line text area instead of a standard textbox. -In this way, you can specialize the view model class based on your UI requirements without touching to the DTO. As a result of this decision, we have used `ObjectMapper` to map `CreateAuthorViewModel` to `CreateAuthorDto`. To be able to do that, you need to add a new mapping code to the `BookStoreWebAutoMapperProfile` constructor: +In this way, you can specialize the view model class based on your UI requirements without touching to the DTO. As a result of this decision, we have used `ObjectMapper` to map `CreateAuthorViewModel` to `CreateAuthorDto`. To be able to do that, you need to define a new mapping configuration in the `BookStoreWebMappers` class: -````csharp -using Acme.BookStore.Authors; // ADDED NAMESPACE IMPORT -using Acme.BookStore.Books; -using AutoMapper; - -namespace Acme.BookStore.Web; - -public class BookStoreWebAutoMapperProfile : Profile +```csharp +[Mapper] +public partial class CreateAuthorViewModelToCreateAuthorDtoMapper : MapperBase { - public BookStoreWebAutoMapperProfile() - { - CreateMap(); - - // ADD a NEW MAPPING - CreateMap(); - } + public override partial CreateAuthorDto Map(Pages.Authors.CreateModalModel.CreateAuthorViewModel source); + public override partial void Map(Pages.Authors.CreateModalModel.CreateAuthorViewModel source, CreateAuthorDto destination); } -```` +``` "New author" button will work as expected and open a new model when you run the application again: @@ -456,29 +445,22 @@ This class is similar to the `CreateModal.cshtml.cs` while there are some main d * Uses the `IAuthorAppService.GetAsync(...)` method to get the editing author from the application layer. * `EditAuthorViewModel` has an additional `Id` property which is marked with the `[HiddenInput]` attribute that creates a hidden input for this property. -This class requires to add two object mapping declarations to the `BookStoreWebAutoMapperProfile` class: +This class requires to add two object mapping declarations, so open the `BookStoreWebMappers` class and add the following mappings: ```csharp -using Acme.BookStore.Authors; -using Acme.BookStore.Books; -using AutoMapper; - -namespace Acme.BookStore.Web; - -public class BookStoreWebAutoMapperProfile : Profile +[Mapper] +public partial class AuthorDtoToEditAuthorViewModelMapper : MapperBase { - public BookStoreWebAutoMapperProfile() - { - CreateMap(); + public override partial EditAuthorViewModel Map(AuthorDto source); - CreateMap(); + public override partial void Map(AuthorDto source, EditAuthorViewModel destination); +} - // ADD THESE NEW MAPPINGS - CreateMap(); - CreateMap(); - } +[Mapper] +public partial class EditAuthorViewModelToUpdateAuthorDtoMapper : MapperBase +{ + public override partial UpdateAuthorDto Map(Pages.Authors.EditModalModel.EditAuthorViewModel source); + public override partial void Map(Pages.Authors.EditModalModel.EditAuthorViewModel source, UpdateAuthorDto destination); } ``` @@ -1220,13 +1202,23 @@ This class typically defines the properties and methods used by the `Authors.raz `Authors` class uses the `IObjectMapper` in the `OpenEditAuthorModal` method. So, we need to define this mapping. -Open the `BookStoreBlazorAutoMapperProfile.cs` in the {{ if UI == "BlazorServer" }}`Acme.BookStore.Blazor`{{ else if UI == "MAUIBlazor" }}`Acme.BookStore.MauiBlazor`{{ else }}`Acme.BookStore.Blazor.Client`{{ end }} project and add the following mapping code in the constructor: +Open the `BookStoreBlazorMappers.cs` in the {{ if UI == "BlazorServer" }}`Acme.BookStore.Blazor`{{ else if UI == "MAUIBlazor" }}`Acme.BookStore.MauiBlazor`{{ else }}`Acme.BookStore.Blazor.Client`{{ end }} project and add the following mappings in the class: -````csharp -CreateMap(); -```` +```csharp +using Riok.Mapperly.Abstractions; +using Volo.Abp.Mapperly; +using Acme.BookStore.Authors; + +//... + +[Mapper] +public partial class AuthorDtoToUpdateAuthorDtoMapper : MapperBase +{ + public override partial UpdateAuthorDto Map(AuthorDto source); -You will need to declare a `using Acme.BookStore.Authors;` statement to the beginning of the file. + public override partial void Map(AuthorDto source, UpdateAuthorDto destination); +} +``` ### Add to the Main Menu diff --git a/docs/en/tutorials/book-store/part-10.md b/docs/en/tutorials/book-store/part-10.md index d4137e3681..02d1f81aba 100644 --- a/docs/en/tutorials/book-store/part-10.md +++ b/docs/en/tutorials/book-store/part-10.md @@ -578,11 +578,17 @@ Let's see the changes we've done: ### Object to Object Mapping Configuration -Introduced the `AuthorLookupDto` class and used object mapping inside the `GetAuthorLookupAsync` method. So, we need to add a new mapping definition inside the `BookStoreApplicationAutoMapperProfile.cs` file of the `Acme.BookStore.Application` project: +Introduced the `AuthorLookupDto` class and used object mapping inside the `GetAuthorLookupAsync` method. So, we need to add a new mapping definition inside the `BookStoreApplicationMappers.cs` file of the `Acme.BookStore.Application` project: -````csharp -CreateMap(); -```` +```csharp +[Mapper] +public partial class AuthorToAuthorLookupDtoMapper : MapperBase +{ + public override partial AuthorLookupDto Map(Author source); + + public override partial void Map(Author source, AuthorLookupDto destination); +} +``` ## Unit Tests @@ -898,12 +904,37 @@ These changes require a small change in the `EditModal.cshtml`. Remove the `(); -CreateMap(); -CreateMap(); +using Riok.Mapperly.Abstractions; +using Volo.Abp.Mapperly; + +//... + +[Mapper] +public partial class CreateBookViewModelToCreateUpdateBookDtoMapper : MapperBase +{ + public override partial CreateUpdateBookDto Map(Pages.Books.CreateModalModel.CreateBookViewModel source); + + public override partial void Map(Pages.Books.CreateModalModel.CreateBookViewModel source, CreateUpdateBookDto destination); +} + +[Mapper] +public partial class BookDtoToEditBookViewModelMapper : MapperBase +{ + public override partial Pages.Books.EditModalModel.EditBookViewModel Map(BookDto source); + + public override partial void Map(BookDto source, Pages.Books.EditModalModel.EditBookViewModel destination); +} + +[Mapper] +public partial class EditBookViewModelToCreateUpdateBookDtoMapper : MapperBase +{ + public override partial CreateUpdateBookDto Map(Pages.Books.EditModalModel.EditBookViewModel source); + + public override partial void Map(Pages.Books.EditModalModel.EditBookViewModel source, CreateUpdateBookDto destination); +} ``` You can run the application and try to create a new book or update an existing book. You will see a drop down list on the create/update form to select the author of the book: diff --git a/docs/en/tutorials/microservice/part-05.md b/docs/en/tutorials/microservice/part-05.md index 9c037617a1..e72e94fc66 100644 --- a/docs/en/tutorials/microservice/part-05.md +++ b/docs/en/tutorials/microservice/part-05.md @@ -255,21 +255,20 @@ public class OrderAppService : ApplicationService, IOrderAppService In this code snippet, we inject the `IRepository` into the `OrderAppService` class. We use this repository to interact with the `Order` entity. The `GetListAsync` method retrieves a list of orders from the database and maps them to the `OrderDto` class. The `CreateAsync` method creates a new order entity and inserts it into the database. -Afterward, we need to configure the *AutoMapper* object to map the `Order` entity to the `OrderDto` class. Open the `OrderingServiceApplicationAutoMapperProfile` class in the `CloudCrm.OrderingService` project, located in the `ObjectMapping` folder, and add the following code: +Afterward, we need to configure the *Mapperly* object to map the `Order` entity to the `OrderDto` class. Open the `OrderingServiceApplicationMappers` class in the `CloudCrm.OrderingService` project, located in the `ObjectMapping` folder, and add the following code: ```csharp -using AutoMapper; -using CloudCrm.OrderingService.Entities; -using CloudCrm.OrderingService.Services; +using Riok.Mapperly.Abstractions; +using Volo.Abp.Mapperly; namespace CloudCrm.OrderingService.ObjectMapping; -public class OrderingServiceApplicationAutoMapperProfile : Profile +[Mapper] +public partial class OrderingServiceApplicationMappers : MapperBase { - public OrderingServiceApplicationAutoMapperProfile() - { - CreateMap(); - } + public override partial OrderDto Map(Order source); + + public override partial void Map(Order source, OrderDto destination); } ``` diff --git a/docs/en/tutorials/microservice/part-06.md b/docs/en/tutorials/microservice/part-06.md index e5a8f53fe5..f268ba6fff 100644 --- a/docs/en/tutorials/microservice/part-06.md +++ b/docs/en/tutorials/microservice/part-06.md @@ -216,25 +216,25 @@ public class OrderDto } ``` -Lastly, open the `OrderingServiceApplicationAutoMapperProfile` class (the `OrderingServiceApplicationAutoMapperProfile.cs` file under the `ObjectMapping` folder of the `CloudCrm.OrderingService` project of the `CloudCrm.OrderingService` .NET solution) and ignore the `ProductName` property in the mapping configuration: +Lastly, open the `OrderingServiceApplicationMappers` class (the `OrderingServiceApplicationMappers.cs` file under the `ObjectMapping` folder of the `CloudCrm.OrderingService` project of the `CloudCrm.OrderingService` .NET solution) and ignore the `ProductName` property in the mapping configuration: ```csharp -using AutoMapper; -using CloudCrm.OrderingService.Entities; -using CloudCrm.OrderingService.Services; -using Volo.Abp.AutoMapper; +using Riok.Mapperly.Abstractions; +using Volo.Abp.Mapperly; namespace CloudCrm.OrderingService.ObjectMapping; -public class OrderingServiceApplicationAutoMapperProfile : Profile +[Mapper] +public partial class OrderingServiceApplicationMappers : MapperBase { - public OrderingServiceApplicationAutoMapperProfile() - { - CreateMap() - .Ignore(x => x.ProductName); // New line - } + [MapperIgnoreTarget(nameof(OrderDto.ProductName))] + public override partial OrderDto Map(Order source); + + [MapperIgnoreTarget(nameof(OrderDto.ProductName))] + public override partial void Map(Order source, OrderDto destination); } ``` + Let's explain the changes we made: - We added a new property named `ProductName` to the `OrderDto` class. This property will hold the product name. diff --git a/docs/en/tutorials/modular-crm/part-03.md b/docs/en/tutorials/modular-crm/part-03.md index fb19277895..bbb0339b50 100644 --- a/docs/en/tutorials/modular-crm/part-03.md +++ b/docs/en/tutorials/modular-crm/part-03.md @@ -323,23 +323,17 @@ Notice that `ProductAppService` class implements the `IProductAppService` and al #### Object Mapping -`ProductAppService.GetListAsync` method uses the `ObjectMapper` service to convert `Product` entities to `ProductDto` objects. The mapping should be configured. Open the `CatalogAutoMapperProfile` class in the `ModularCrm.Catalog` project and change it to the following code block: +`ProductAppService.GetListAsync` method uses the `ObjectMapper` service to convert `Product` entities to `ProductDto` objects. The mapping should be configured. So, create a new mapping class in the `ModularCrm.Catalog` project that implements the `MapperBase` class with the `[Mapper]` attribute as follows: -````csharp -using AutoMapper; - -namespace ModularCrm.Catalog; - -public class CatalogAutoMapperProfile : Profile +```csharp +[Mapper] +public partial class ProductToProductDtoMapper : MapperBase { - public CatalogAutoMapperProfile() - { - CreateMap(); - } -} -```` + public override partial ProductDto Map(Product source); -We've added the `CreateMap();` line to define the mapping. + public override partial void Map(Product source, ProductDto destination); +} +``` ### Exposing Application Services as HTTP API Controllers diff --git a/docs/en/tutorials/modular-crm/part-05.md b/docs/en/tutorials/modular-crm/part-05.md index d2082667aa..46a693a963 100644 --- a/docs/en/tutorials/modular-crm/part-05.md +++ b/docs/en/tutorials/modular-crm/part-05.md @@ -283,21 +283,17 @@ The new files under the `ModularCrm.Ordering.Contracts` project should be like t ### Implementing the Application Service -First we configure the *AutoMapper* to map the `Order` entity to the `OrderDto` object, because we will need it later. Open the `OrderingAutoMapperProfile` under the `ModularCrm.Ordering` project: +First, create a new mapping class (under the `ModularCrm.Ordering` project) that implements the `MapperBase` class with the `[Mapper]` attribute to map `Order` entities to `OrderDto` objects as follows, because we will need it later: -````csharp -using AutoMapper; - -namespace ModularCrm.Ordering; - -public class OrderingAutoMapperProfile : Profile +```csharp +[Mapper] +public partial class OrderToOrderDtoMapper : MapperBase { - public OrderingAutoMapperProfile() - { - CreateMap(); - } + public override partial OrderDto Map(Order source); + + public override partial void Map(Order source, OrderDto destination); } -```` +``` Now, you can implement the `IOrderAppService` interface. Create an `OrderAppService` class under the `ModularCrm.Ordering` project: diff --git a/docs/en/tutorials/modular-crm/part-06.md b/docs/en/tutorials/modular-crm/part-06.md index b685d72960..1c04cd56d1 100644 --- a/docs/en/tutorials/modular-crm/part-06.md +++ b/docs/en/tutorials/modular-crm/part-06.md @@ -217,21 +217,17 @@ public class OrderDto } ```` -Lastly, open the `OrderingAutoMapperProfile` class (the `OrderingAutoMapperProfile.cs` file under the `Services` folder of the `ModularCrm.Ordering` project of the `ModularCrm.Ordering` .NET solution) and ignore the `ProductName` property in the mapping configuration: +Lastly, open the `OrderingApplicationMappers` class (the `OrderingApplicationMappers.cs` file under the `Services` folder of the `ModularCrm.Ordering` project of the `ModularCrm.Ordering` .NET solution) and add the following mapping class: ````csharp -using AutoMapper; -using Volo.Abp.AutoMapper; - -namespace ModularCrm.Ordering; - -public class OrderingApplicationAutoMapperProfile : Profile +[Mapper] +public partial class OrderToOrderDtoMapper : MapperBase { - public OrderingApplicationAutoMapperProfile() - { - CreateMap() - .Ignore(x => x.ProductName); // New line - } + [MapperIgnoreTarget(nameof(OrderDto.ProductName))] + public override partial OrderDto Map(Order source); + + [MapperIgnoreTarget(nameof(OrderDto.ProductName))] + public override partial void Map(Order source, OrderDto destination); } ````