How to use AutoMapper in .NET
TLDR
- AutoMapper simplifies property assignment between DTOs and Entities via Reflection, reducing boilerplate code.
MapperConfigurationshould be maintained as a Singleton; it is recommended to configure it during application startup (e.g.,Program.cs).- Always use
config.AssertConfigurationIsValid()to validate mapping settings and ensure all Destination fields have corresponding sources. - Use the
Profileclass to modularize mapping logic and prevent configuration bloat. - For complex mappings, use
ForMemberto handle conditional logic, ignore fields, or define custom transformation logic. - Use
ReverseMap()to simplify bidirectional conversion, but note that complex logic requiresForPathto define reverse rules. - To integrate with Dependency Injection, install the
AutoMapper.Extensions.Microsoft.DependencyInjectionpackage.
Basic Configuration and Validation of AutoMapper
In .NET applications, to achieve separation of concerns, it is often necessary to convert DTOs between different layers. AutoMapper defines mapping rules through MapperConfiguration and executes conversions via IMapper.
Core Usage
When to use this: When you need to copy property values from one object type to another.
// Create Configuration to define mapping relationships between classes
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Order, OrderDto>();
});
// Validate the Configuration settings; throws AutoMapperConfigurationException if a Member in Destination cannot be found in Source
config.AssertConfigurationIsValid();
// Create Mapper
var mapper = config.CreateMapper();
// Execute mapping
var dest = mapper.Map<Destination>(source);Mapping Rule Configuration
To keep the code clean, it is recommended to encapsulate mapping settings within a Profile class.
Modular Configuration
When to use this: When there are a large number of class mappings in the project, causing Program.cs or Startup.cs to become bloated.
public class OtherProfile : Profile {
public OtherProfile() {
CreateMap<Source1, Destination1>();
// Global type conversion (e.g., uniformly trimming strings)
CreateMap<string?, string?>().ConvertUsing(x => x == null ? x : x.Trim());
}
}Advanced Property Mapping Control
When to use this: When property names in Source and Destination do not match, or when you need to decide whether to map based on specific conditions.
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Source, Destination>()
// Ignore specific fields
.ForMember(desc => desc.Prop1, opt => opt.Ignore())
// Specify field mapping
.ForMember(dest => dest.DestProp2, opt => opt.MapFrom(src => src.SourceProp2))
// Set conditional mapping
.ForMember(dest => dest.IntProp4, opt => opt.Condition(src => (src.IntProp4 >= 0)))
// Set default value
.ForMember(dest => dest.Prop5, opt => opt.NullSubstitute("Other Value"));
});Reverse Mapping and Considerations
Using ReverseMap() can quickly establish bidirectional mapping, but be aware of the following limitations:
- Complex conversion logic requires
ForPathto define reverse rules. AssertConfigurationIsValid()cannot verify the correctness of reverse mappings.
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Source, Destination>()
.ForMember(dest => dest.Prop5, opt => opt.MapFrom(src => src.Prop3 + "," + src.Prop4))
.ReverseMap()
.ForPath(s => s.Prop3, opt => opt.MapFrom(src => src.Prop5.Split(',')[0]));
});Integrating Dependency Injection
When to use this: In modern .NET applications, you need to manage IMapper instances via a DI container.
You must install the NuGet package AutoMapper.Extensions.Microsoft.DependencyInjection.
// Register in Program.cs
builder.Services.AddAutoMapper(typeof(Program));
// Use in Controller or Service
public class EmployeesController {
private readonly IMapper _mapper;
public EmployeesController(IMapper mapper) => _mapper = mapper;
}Changelog
- 2022-10-24 Initial documentation created.
