Skip to content

Commit daca929

Browse files
committed
Merge remote-tracking branch 'origin/dev'
2 parents 81cb09e + baef8a2 commit daca929

57 files changed

Lines changed: 1622 additions & 213 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
167 KB
Loading

docs/en/Core-Mvc-Elsa-Integration.md

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -92,33 +92,37 @@ This will allow Elsa to use ASP.NET Zero application for handling HTTP activitie
9292

9393
There are some special configuration points here which doesn't exist in default Elsa document. Let's take a look at those sections one by one;
9494

95-
#### AutoMapper
95+
#### Object Mapping
96+
97+
ASP.NET Zero uses [Mapperly](https://mapperly.riok.app/) for object-to-object mappings, which generates mapping code at compile-time. Elsa, on the other hand, uses AutoMapper internally for its own mappings.
98+
99+
To allow both to coexist, we configure Elsa to manage its own AutoMapper instance:
96100

97101
````c#
98102
elsa.UseAutoMapper(() => { });
99103
````
100104

101-
Both Elsa and ASP.NET Boilerplate creates a `MapperConfiguration` and uses it to create an `IMapper`. Because of this, if we don't add this line to Elsa configuration, the app will only use Elsa's **AutoMapper** configuration. But, after making this configuration, we need to configure Elsa's AutoMapper mappings in our app manually. In order to do this, go to `*WebMvcModule` and add below lines to its `PreInitialize` method.
105+
This configuration tells Elsa to use an empty AutoMapper configuration, allowing it to set up its internal mappings without conflicting with ASP.NET Zero's Mapperly-based mappers.
102106

103-
```
104-
// ELSA AutoMapper
105-
Configuration.Modules.AbpAutoMapper().Configurators.Add(config =>
107+
If you need to create custom mappings for Elsa types in your application, create a dedicated Mapperly mapper:
108+
109+
```csharp
110+
using Riok.Mapperly.Abstractions;
111+
using Elsa.Models;
112+
using Elsa.Services.Models;
113+
114+
[Mapper]
115+
public partial class ElsaWorkflowMapper
106116
{
107-
// AutoMapperProfile
108-
config.CreateMap<IWorkflowBlueprint, WorkflowBlueprintModel>();
109-
config.CreateMap<IWorkflowBlueprint, WorkflowBlueprintSummaryModel>();
110-
config.CreateMap<IActivityBlueprint, ActivityBlueprintModel>().ConvertUsing<ActivityBlueprintConverter>();
111-
config.CreateMap<ICompositeActivityBlueprint, CompositeActivityBlueprintModel>();
112-
config.CreateMap<IConnection, ConnectionModel>().ConvertUsing<ConnectionConverter>();
113-
config.CreateMap<WorkflowInstance, WorkflowInstanceSummaryModel>();
114-
config.CreateMap<WorkflowDefinition, WorkflowDefinitionSummaryModel>();
115-
116-
// CloningProfile
117-
config.CreateMap<WorkflowDefinition, WorkflowDefinition>();
118-
config.CreateMap<WorkflowInstance, WorkflowInstance>();
119-
});
117+
public partial WorkflowBlueprintModel Map(IWorkflowBlueprint source);
118+
public partial WorkflowBlueprintSummaryModel MapToSummary(IWorkflowBlueprint source);
119+
public partial WorkflowInstanceSummaryModel Map(WorkflowInstance source);
120+
public partial WorkflowDefinitionSummaryModel Map(WorkflowDefinition source);
121+
}
120122
```
121123

124+
> **Note:** Elsa's internal operations use its own AutoMapper configuration. The mapper above is only needed if you want to map Elsa types in your own application code.
125+
122126
#### API Versioning
123127

124128
```

docs/en/Developing-Step-By-Step-Angular-Adding-AddPhone-DeletePhone-Methods.md

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,32 @@ public class PhoneConsts
3737
}
3838
```
3939

40+
First, create a mapper for the phone operations. Create `AddPhoneInputToPhoneMapper.cs`:
41+
42+
```csharp
43+
using Riok.Mapperly.Abstractions;
44+
45+
namespace Acme.PhoneBookDemo.PhoneBook.Mapper;
46+
47+
[Mapper]
48+
public partial class AddPhoneInputToPhoneMapper
49+
{
50+
public partial Phone Map(AddPhoneInput input);
51+
}
52+
```
53+
4054
Now, we can implement these methods:
4155

4256
```csharp
57+
private readonly IRepository<Person> _personRepository;
58+
private readonly IRepository<Phone, long> _phoneRepository;
59+
60+
public PersonAppService(IRepository<Person> personRepository, IRepository<Phone, long> phoneRepository)
61+
{
62+
_personRepository = personRepository;
63+
_phoneRepository = phoneRepository;
64+
}
65+
4366
[AbpAuthorize(AppPermissions.Pages_Tenant_PhoneBook_EditPerson)]
4467
public async Task DeletePhone(EntityDto<long> input)
4568
{
@@ -62,12 +85,6 @@ public async Task<PhoneInPersonListDto> AddPhone(AddPhoneInput input)
6285
}
6386
```
6487

65-
Then we add configuration for AutoMapper into CustomDtoMapper.cs like below:
66-
67-
```csharp
68-
configuration.CreateMap<AddPhoneInput, Phone>();
69-
```
70-
7188
A permission should have a unique name. We define permission names as constant strings in **AppPermissions** class. It's a simple constant string:
7289

7390
```csharp

docs/en/Developing-Step-By-Step-Angular-Changing-GetPeople-Method.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,24 @@ public class PhoneInPersonListDto : CreationAuditedEntityDto<long>
2525
}
2626
```
2727

28-
Then we add configuration for AutoMapper into CustomDtoMapper.cs like below:
28+
Then we create a mapper for Phone to PhoneInPersonListDto. Create a new file `PhoneToPhoneInPersonListDtoMapper.cs`:
2929

3030
```csharp
31-
configuration.CreateMap<Phone, PhoneInPersonListDto>();
31+
using Riok.Mapperly.Abstractions;
32+
33+
namespace Acme.PhoneBookDemo.PhoneBook.Mapper;
34+
35+
[Mapper]
36+
public partial class PhoneToPhoneInPersonListDtoMapper
37+
{
38+
public partial PhoneInPersonListDto Map(Phone phone);
39+
public partial Collection<PhoneInPersonListDto> Map(ICollection<Phone> phones);
40+
}
3241
```
3342

34-
So, added also a DTO to transfer phone numbers and mapped from Phone
35-
entity. Now, we can change GetPeople method to get Phones from database:
43+
Mapperly will automatically map the nested `Phones` collection since both the source and target property names match.
44+
45+
Now, we can change GetPeople method to get Phones from database:
3646

3747
```csharp
3848
public ListResultDto<PersonListDto> GetPeople(GetPeopleInput input)

docs/en/Developing-Step-By-Step-Angular-Creating-New-Person.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,21 @@ public class CreatePersonInput
3131
}
3232
```
3333

34-
Then we add configuration for AutoMapper into CustomDtoMapper.cs like below:
34+
Then we create a mapper for CreatePersonInput to Person. Create a new file `CreatePersonInputToPersonMapper.cs`:
3535

3636
```csharp
37-
configuration.CreateMap<CreatePersonInput, Person>();
37+
using Riok.Mapperly.Abstractions;
38+
39+
namespace Acme.PhoneBookDemo.PhoneBook.Mapper;
40+
41+
[Mapper]
42+
public partial class CreatePersonInputToPersonMapper
43+
{
44+
public partial Person Map(CreatePersonInput input);
45+
}
3846
```
3947

40-
**CreatePersonInput** is mapped to **Person** entity (comment out
41-
related line in CustomDtoMapper.cs and we will use mapping below).
48+
**CreatePersonInput** is mapped to **Person** entity using Mapperly.
4249
All properties are decorated with **data annotation attributes**
4350
to provide automatic
4451
**[validation](https://aspnetboilerplate.com/Pages/Documents/Validating-Data-Transfer-Objects)**.
@@ -67,7 +74,7 @@ public async Task CreatePerson(CreatePersonInput input)
6774
```
6875

6976
A Person entity is created by mapping given input, then inserted to
70-
database. We used **async/await** pattern here. All methods in ASP.NET
77+
database. The `ObjectMapper` uses the Mapperly-generated mapper internally. We used **async/await** pattern here. All methods in ASP.NET
7178
Zero startup project is **async**. It's advised to use async/await
7279
wherever possible.
7380

docs/en/Developing-Step-By-Step-Angular-Creating-Person-Application-Service.md

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,25 @@ public class PersonListDto : FullAuditedEntityDto
3434
}
3535
```
3636

37-
**CustomDtoMapper.cs** is used to create mapping from **Person** to **PersonListDto**. **FullAuditedEntityDto** is inherited to implement audit properties automatically. See [application service](https://aspnetboilerplate.com/Pages/Documents/Application-Services) and [DTO](https://aspnetboilerplate.com/Pages/Documents/Data-Transfer-Objects) documentations for more information. We are adding the following mappings.
37+
We need to create a mapper to convert **Person** to **PersonListDto**. **FullAuditedEntityDto** is inherited to implement audit properties automatically. See [application service](https://aspnetboilerplate.com/Pages/Documents/Application-Services) and [DTO](https://aspnetboilerplate.com/Pages/Documents/Data-Transfer-Objects) documentations for more information.
38+
39+
Create a new file `PersonToPersonListDtoMapper.cs` in the **.Application** project:
3840

3941
```csharp
40-
...
41-
// PhoneBook (we will comment out other lines when the new DTOs are added)
42-
configuration.CreateMap<Person, PersonListDto>();
43-
//configuration.CreateMap<AddPhoneInput, Phone>();
44-
//configuration.CreateMap<CreatePersonInput, Person>();
45-
//configuration.CreateMap<Person, GetPersonForEditOutput>();
46-
//configuration.CreateMap<Phone, PhoneInPersonListDto>();
42+
using Riok.Mapperly.Abstractions;
43+
44+
namespace Acme.PhoneBookDemo.PhoneBook.Mapper;
45+
46+
[Mapper]
47+
public partial class PersonToPersonListDtoMapper
48+
{
49+
public partial PersonListDto Map(Person person);
50+
public partial List<PersonListDto> Map(List<Person> persons);
51+
}
4752
```
4853

54+
ASP.NET Zero automatically discovers and registers this mapper class.
55+
4956
After defining interface, we can implement it as shown below: (in **.Application** project)
5057

5158
```csharp
@@ -85,9 +92,9 @@ public class PersonAppService : PhoneBookDemoAppServiceBase, IPersonAppService
8592
}
8693
```
8794

88-
We're injecting **person repository** (it's automatically created by ABP) and using it to filter and get people from database.
95+
We're injecting **person repository** (it's automatically created by ABP).
8996

90-
**WhereIf** is an extension method here (defined in Abp.Linq.Extensions namespace). It performs Where condition, only if filter is not null or empty. **IsNullOrEmpty** is also an extension method (defined in Abp.Extensions namespace). ABP has many similar shortcut extension methods. **ObjectMapper.Map** method automatically converts list of Person entities to list of PersonListDto objects with using configurations in **CustomDtoMapper.cs** in **.Application** project.
97+
**WhereIf** is an extension method here (defined in Abp.Linq.Extensions namespace). It performs Where condition, only if filter is not null or empty. **IsNullOrEmpty** is also an extension method (defined in Abp.Extensions namespace). ABP has many similar shortcut extension methods. **ObjectMapper.Map** automatically converts list of Person entities to list of PersonListDto objects using the Mapperly-generated mapping code internally.
9198

9299
### Connection & Transaction Management
93100

docs/en/Developing-Step-By-Step-Angular-DevExtreme-Creating-New-Person.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,21 @@ public class CreatePersonInput
3131
}
3232
```
3333

34-
Then we add configuration for AutoMapper into CustomDtoMapper.cs like below:
34+
Then we create a mapper for CreatePersonInput to Person. Create a new file `CreatePersonInputToPersonMapper.cs`:
3535

3636
```csharp
37-
configuration.CreateMap<CreatePersonInput, Person>();
37+
using Riok.Mapperly.Abstractions;
38+
39+
namespace Acme.PhoneBookDemo.PhoneBook.Mapper;
40+
41+
[Mapper]
42+
public partial class CreatePersonInputToPersonMapper
43+
{
44+
public partial Person Map(CreatePersonInput input);
45+
}
3846
```
3947

40-
**CreatePersonInput** is mapped to **Person** entity (comment out
41-
related line in CustomDtoMapper.cs and we will use mapping below).
48+
**CreatePersonInput** is mapped to **Person** entity using Mapperly.
4249
All properties are decorated with **data annotation attributes**
4350
to provide automatic
4451
**[validation](https://aspnetboilerplate.com/Pages/Documents/Validating-Data-Transfer-Objects)**.
@@ -67,7 +74,7 @@ public async Task CreatePerson(CreatePersonInput input)
6774
```
6875

6976
A Person entity is created by mapping given input, then inserted to
70-
database. We used **async/await** pattern here. All methods in ASP.NET
77+
database. The `ObjectMapper` uses the Mapperly-generated mapper internally. We used **async/await** pattern here. All methods in ASP.NET
7178
Zero startup project is **async**. It's advised to use async/await
7279
wherever possible.
7380

docs/en/Developing-Step-By-Step-Angular-DevExtreme-Creating-Person-Application-Service.md

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,25 @@ public class PersonListDto : FullAuditedEntityDto
3535
}
3636
```
3737

38-
**CustomDtoMapper.cs** is used to create mapping from **Person** to **PersonListDto**. **FullAuditedEntityDto** is inherited to implement audit properties automatically. See [application service](https://aspnetboilerplate.com/Pages/Documents/Application-Services) and [DTO](https://aspnetboilerplate.com/Pages/Documents/Data-Transfer-Objects) documentations for more information. We are adding the following mappings.
38+
We need to create a mapper to convert **Person** to **PersonListDto**. **FullAuditedEntityDto** is inherited to implement audit properties automatically. See [application service](https://aspnetboilerplate.com/Pages/Documents/Application-Services) and [DTO](https://aspnetboilerplate.com/Pages/Documents/Data-Transfer-Objects) documentations for more information.
39+
40+
Create a new file `PersonToPersonListDtoMapper.cs` in the **.Application** project:
3941

4042
```csharp
41-
...
42-
// PhoneBook (we will comment out other lines when the new DTOs are added)
43-
configuration.CreateMap<Person, PersonListDto>();
44-
//configuration.CreateMap<AddPhoneInput, Phone>();
45-
//configuration.CreateMap<CreatePersonInput, Person>();
46-
//configuration.CreateMap<Person, GetPersonForEditOutput>();
47-
//configuration.CreateMap<Phone, PhoneInPersonListDto>();
43+
using Riok.Mapperly.Abstractions;
44+
45+
namespace Acme.PhoneBookDemo.PhoneBook.Mapper;
46+
47+
[Mapper]
48+
public partial class PersonToPersonListDtoMapper
49+
{
50+
public partial PersonListDto Map(Person person);
51+
public partial List<PersonListDto> Map(List<Person> persons);
52+
}
4853
```
4954

55+
ASP.NET Zero automatically discovers and registers this mapper class.
56+
5057
After defining interface, we can implement it as shown below: (in **.Application** project)
5158

5259
```csharp
@@ -78,9 +85,9 @@ public class PersonAppService : PhoneBookDemoAppServiceBase, IPersonAppService
7885
}
7986
```
8087

81-
We're injecting **person repository** (it's automatically created by ABP) and using it to filter and get people from database.
88+
We're injecting **person repository** (it's automatically created by ABP).
8289

83-
**WhereIf** is an extension method here (defined in Abp.Linq.Extensions namespace). It performs Where condition, only if filter is not null or empty. **IsNullOrEmpty** is also an extension method (defined in Abp.Extensions namespace). ABP has many similar shortcut extension methods. **ObjectMapper.Map** method automatically converts list of Person entities to list of PersonListDto objects with using configurations in **CustomDtoMapper.cs** in **.Application** project.
90+
**WhereIf** is an extension method here (defined in Abp.Linq.Extensions namespace). It performs Where condition, only if filter is not null or empty. **IsNullOrEmpty** is also an extension method (defined in Abp.Extensions namespace). ABP has many similar shortcut extension methods. **ObjectMapper.Map** automatically converts list of Person entities to list of PersonListDto objects using the Mapperly-generated mapping code internally.
8491

8592
### Connection & Transaction Management
8693

docs/en/Developing-Step-By-Step-Angular-Edit-Mode-People.md

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,10 @@ Now we want to edit name, surname and e-mail of people:
55
<img src="images/edit-person-core1.png" alt="Edit Person" class="img-thumbnail" />
66

77
First of all, we create the necessary DTOs to transfer people's id, name,
8-
surname and e-mail. We can optionally configure auto-mapper, but this is not necessary because all properties match automatically. Then we create the functions in PersonAppService for
8+
surname and e-mail. Then we create the functions in PersonAppService for
99
editing people:
1010

11-
```csharp
12-
[AbpAuthorize(AppPermissions.Pages_Tenant_PhoneBook_EditPerson)]
13-
public async Task<GetPersonForEditOutput> GetPersonForEdit( EntityDto<int> input)
14-
{
15-
var person = await _personRepository.GetAsync(input.Id);
16-
return ObjectMapper.Map<GetPersonForEditOutput>(person);
17-
}
18-
19-
[AbpAuthorize(AppPermissions.Pages_Tenant_PhoneBook_EditPerson)]
20-
public async Task EditPerson(EditPersonInput input)
21-
{
22-
var person = await _personRepository.FirstOrDefaultAsync(input.Id);
23-
ObjectMapper.Map(input, person);
24-
}
25-
11+
```csharp
2612
public class GetPersonForEditOutput : EntityDto<int>
2713
{
2814
public string Name { get; set; }
@@ -38,11 +24,48 @@ public class EditPersonInput : EntityDto<int>
3824
}
3925
```
4026

41-
Then we add configuration for AutoMapper into CustomDtoMapper.cs like below:
27+
Then we create mappers for the edit operations. Create `PersonToGetPersonForEditOutputMapper.cs`:
28+
29+
```csharp
30+
using Riok.Mapperly.Abstractions;
31+
32+
namespace Acme.PhoneBookDemo.PhoneBook.Mapper;
33+
34+
[Mapper]
35+
public partial class PersonToGetPersonForEditOutputMapper
36+
{
37+
public partial GetPersonForEditOutput Map(Person person);
38+
}
39+
```
40+
41+
Create `EditPersonInputToPersonMapper.cs` for updating existing persons:
4242

4343
```csharp
44-
configuration.CreateMap<Person, GetPersonForEditOutput>();
45-
configuration.CreateMap<EditPersonInput, Person>().ReverseMap();
44+
using Riok.Mapperly.Abstractions;
45+
46+
namespace Acme.PhoneBookDemo.PhoneBook.Mapper;
47+
48+
[Mapper]
49+
public partial class EditPersonInputToPersonMapper
50+
{
51+
public partial void Map(EditPersonInput input, Person person);
52+
}
53+
```
54+
55+
Then use ObjectMapper in the PersonAppService methods:
56+
57+
```csharp
58+
public async Task<GetPersonForEditOutput> GetPersonForEdit(EntityDto input)
59+
{
60+
var person = await _personRepository.GetAsync(input.Id);
61+
return ObjectMapper.Map<GetPersonForEditOutput>(person);
62+
}
63+
64+
public async Task EditPerson(EditPersonInput input)
65+
{
66+
var person = await _personRepository.GetAsync(input.Id);
67+
ObjectMapper.Map(input, person);
68+
}
4669
```
4770

4871
## View

0 commit comments

Comments
 (0)