DTO Mappings
ASP.NET Zero uses Mapperly for DTO to Entity mappings (and other types of object-to-object mappings). Mapperly is a .NET source generator that generates mapping code at compile-time, providing better performance and type safety compared to runtime reflection-based mappers.
Creating a Mapper
To create a mapping between two types, create a mapper class with the [Mapper] attribute. ASP.NET Zero uses per-functionality mapper classes following the naming convention {SourceType}To{DestinationType}Mapper.
For instance, see the mapper class that converts a Tenant entity to TenantEditDto:
using Riok.Mapperly.Abstractions;
namespace Acme.PhoneBookDemo.MultiTenancy.Mapper;
[Mapper]
public partial class TenantToTenantEditDtoMapper
{
public partial TenantEditDto Map(Tenant tenant);
}
The DTO class:
public class TenantEditDto : EntityDto
{
[Required]
[StringLength(Tenant.MaxTenancyNameLength)]
public string TenancyName { get; set; }
[Required]
[StringLength(Tenant.MaxNameLength)]
public string Name { get; set; }
public bool IsActive { get; set; }
}
Mapperly automatically generates the mapping implementation at compile-time by matching property names. ASP.NET Zero automatically discovers and registers these mapper classes.
Using the Mapper
Use ObjectMapper.Map<T>() to convert objects. The ObjectMapper uses the Mapperly-generated mappers internally:
[AbpAuthorize(AppPermissions.Pages_Tenants_Edit)]
public async Task<TenantEditDto> GetTenantForEdit(EntityRequestInput input)
{
var tenant = await TenantManager.GetByIdAsync(input.Id);
return ObjectMapper.Map<TenantEditDto>(tenant);
}
The ObjectMapper property is available in base classes (like AppServiceBase), or can be injected as IObjectMapper where needed.
Collection Mapping
When you define a mapper method for a single object, Mapperly can automatically generate collection mappings:
[Mapper]
public partial class PersonToPersonListDtoMapper
{
public partial PersonListDto Map(Person person);
public partial List<PersonListDto> Map(List<Person> persons);
}
Usage:
return new ListResultDto<PersonListDto>(ObjectMapper.Map<List<PersonListDto>>(persons));
Updating Existing Objects
To update an existing object instead of creating a new one, use ObjectMapper.Map with both source and target:
public async Task EditPerson(EditPersonInput input)
{
var person = await _personRepository.FirstOrDefaultAsync(input.Id);
ObjectMapper.Map(input, person);
}
The corresponding mapper class:
[Mapper]
public partial class EditPersonInputToPersonMapper
{
public partial void Map(EditPersonInput input, Person person);
}
Custom Property Mapping
When property names don't match, use the [MapProperty] attribute:
[Mapper]
public partial class UserToUserDtoMapper
{
[MapProperty(nameof(User.EmailAddress), nameof(UserDto.Email))]
public partial UserDto Map(User user);
}
Ignoring Properties
To ignore specific properties during mapping:
[Mapper]
public partial class UserToUserDtoMapper
{
[MapperIgnoreSource(nameof(User.Password))]
[MapperIgnoreTarget(nameof(UserDto.FullName))]
public partial UserDto Map(User user);
}
Benefits of Mapperly
- Compile-time code generation: No runtime reflection overhead
- Type safety: Mapping errors are caught at compile time
- AOT compatibility: Works with Native AOT compilation
- Performance: Generated code is as fast as hand-written mapping
- Debuggable: Generated code can be inspected and debugged
See Mapperly documentation for more advanced mapping scenarios.
See Data Transfer Objects documentation for more information on DTOs.