AutoMapper vs. Manual Mapping in .NET

Vitor Gomes
3 min read4 days ago

--

When working with object-to-object mapping in .NET, developers often debate between using AutoMapper a powerful library for automatic mapping and writing manual mappings. To see how these approaches compare in terms of performance, I set up a BenchmarkDotNet test for the four data structures described below.

The Setup

I created:

  • Source and Destination classes.
  • Source and Destination records.
  • Source and Destination record structs.

All share the same set of properties:

  • Basic fields like Id, Name, Description, CreatedAt
  • Ten extra fields (Field1, Field2, etc.) of various types (int, string, double, decimal, bool, DateTime, Guid, long, float, char).

Mapping Approaches

AutoMapper
We configured mappings for each source/destination pair:

var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<SourceClass, DestinationClass>();
cfg.CreateMap<SourceRecord, DestinationRecord>();
cfg.CreateMap<SourceRecordStruct, DestinationRecordStruct>();
});

The library handles property assignments under the hood, making code concise but introducing a performance overhead.

Manual Mapping
We explicitly assign properties in code. For example, mapping from a SourceClass to a DestinationClass,DestinationRecord and DestinationRecordStruct:

public class SourceClass
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime CreatedAt { get; set; }
public int Field1 { get; set; }
public string Field2 { get; set; }
public double Field3 { get; set; }
public decimal Field4 { get; set; }
public bool Field5 { get; set; }
public DateTime Field6 { get; set; }
public Guid Field7 { get; set; }
public long Field8 { get; set; }
public float Field9 { get; set; }
public char Field10 { get; set; }
}
public class DestinationClass
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime CreatedAt { get; set; }
public int Field1 { get; set; }
public string Field2 { get; set; }
public double Field3 { get; set; }
public decimal Field4 { get; set; }
public bool Field5 { get; set; }
public DateTime Field6 { get; set; }
public Guid Field7 { get; set; }
public long Field8 { get; set; }
public float Field9 { get; set; }
public char Field10 { get; set; }
}
public record DestinationRecord(
int Id,
string Name,
string Description,
DateTime CreatedAt,
int Field1,
string Field2,
double Field3,
decimal Field4,
bool Field5,
DateTime Field6,
Guid Field7,
long Field8,
float Field9,
char Field10
);
public readonly record struct DestinationRecordStruct(
int Id,
string Name,
string Description,
DateTime CreatedAt,
int Field1,
string Field2,
double Field3,
decimal Field4,
bool Field5,
DateTime Field6,
Guid Field7,
long Field8,
float Field9,
char Field10
)

Benchmark Configuration

  • BenchmarkDotNet: We used [MemoryDiagnoser] to measure allocations and [SimpleJob(RuntimeMoniker.Net90)] to run on .NET 9.0.
  • Each mapping method is a separate benchmark:
  • AutoMapperMapping() Vs. ManualMapping()
  • AutoMapperMappingRecord() Vs. ManualMappingRecord()
  • AutoMapperMappingRecordStruct() Vs. ManualMappingRecordStruct()

The Results

Here is the BenchmarkDotNet output. Notice both the Meantime (in nanoseconds) and the Allocated memory (in bytes):

Observations

Manual Mapping is notably faster across the board — by a factor of ~3 to 10.

Record Struct sees the largest performance gap:

  • AutoMapper ~96 ns
  • Manual mapping ~8.5 ns

Memory Allocations:

  • AutoMapper consistently allocates ~120 bytes.
  • Manual mapping with records and record structs shows no additional heap allocation (- in the table).

Analyzing the Trade-Offs

Convenience vs. Performance
AutoMapper reduces boilerplate and can make code more readable. However, it introduces overhead in both CPU cycles and memory allocations.

Context Matters

  • If you rarely map large numbers of objects in performance-critical paths, the overhead is likely negligible.
  • If your application needs to map thousands or millions of objects per second, the manual approach (especially with record structs) can yield significant performance gains.

Maintainability
Manual mapping can become cumbersome if you have many properties or types. Each new property requires a code change. AutoMapper automatically maps properties with matching names, reducing maintenance effort.

Conclusion

Which should you choose? It depends on your specific use case:

  • Use AutoMapper if you value simplicity, maintainability, and developer productivity and are not hitting performance bottlenecks in mapping.
  • Use Manual Mapping if every nanosecond counts, in real-time or high-throughput scenarios and you’re willing to invest in writing and maintaining explicit mapping code.

By running these benchmarks, we see a clear performance advantage for manual mapping, especially with record structs. However, this may not be the deciding factor in every application. Evaluate your needs, measure real-world performance, and pick the approach that best balances speed and maintainability.

--

--

Vitor Gomes
Vitor Gomes

Responses (2)