Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save emonarafat/143eef410bf8933a73fd2e4e6a1db7b1 to your computer and use it in GitHub Desktop.

Select an option

Save emonarafat/143eef410bf8933a73fd2e4e6a1db7b1 to your computer and use it in GitHub Desktop.
🧠 AutoMapper vs Manual vs Mapster β€” Real-World Mapping in .NET

🧠 AutoMapper vs Manual vs Mapster β€” Real-World Mapping in .NET

This Gist explores three mapping strategies in .NET: AutoMapper, Manual Mapping, and Mapster. Includes benchmarks and testing guidance.


πŸ”„ AutoMapper Mapping

// Create an AutoMapper profile for mapping Product to ProductDto
public class ProductProfile : Profile
{
    public ProductProfile()
    {
        CreateMap<Product, ProductDto>();
    }
}

⚠️ Drawback: Mapping logic is hidden and can silently break when models change.


βœ… Manual Mapping (Extension Method)

// Extension method for clean, explicit mapping from Product to ProductDto
public static class ProductMapper
{
    public static ProductDto ToDto(this Product p) =>
        new ProductDto
        {
            Id = p.Id,
            Name = p.Name,
            Price = p.Price
        };
}

βœ… Benefits:

  • Compile-time safety
  • Refactorable and searchable in IDE
  • Simple to test

πŸš€ Mapster Mapping (Source Gen)

// Using Mapster's fluent config to map properties explicitly
TypeAdapterConfig<Product, ProductDto>.NewConfig()
    .Map(dest => dest.Name, src => src.ProductName)
    .Map(dest => dest.Price, src => src.Price);

⚑ Fast and clear β€” ideal for repetitive and performance-sensitive mapping.


πŸ“Š Benchmark: AutoMapper vs Manual vs Mapster

// BenchmarkDotNet-based mapping performance comparison
[MemoryDiagnoser]
public class MappingBenchmarks
{
    private readonly Product _product = new() { Id = 1, Name = "Laptop", Price = 999 };

    [Benchmark]
    public ProductDto ManualMapping() => _product.ToDto(); // Fastest

    [Benchmark]
    public ProductDto MapsterMapping() => _product.Adapt<ProductDto>(); // Balanced

    [Benchmark]
    public ProductDto AutoMapperMapping()
    {
        var config = new MapperConfiguration(cfg => cfg.CreateMap<Product, ProductDto>());
        var mapper = config.CreateMapper();
        return mapper.Map<ProductDto>(_product); // Slowest
    }
}

πŸ§ͺ Run it with:

BenchmarkRunner.Run<MappingBenchmarks>();

πŸ§ͺ Manual Mapping Unit Test

// Basic test to ensure mapping outputs expected values
[Fact]
public void MapsProductToDto_Correctly()
{
    var product = new Product { Id = 1, Name = "Laptop", Price = 999 };
    var dto = product.ToDto();

    dto.Id.ShouldBe(1);
    dto.Name.ShouldBe("Laptop");
    dto.Price.ShouldBe(999);
}

🚫 AutoMapper Pitfall: Nested Mapping

public class Order { public Customer Customer { get; set; } }
public class OrderDto { public string CustomerName { get; set; } }

πŸ‘Ž This won't map unless nested mapping is manually configured β€” easy to miss.


🧠 Reverse Mapping Trap

cfg.CreateMap<Product, ProductDto>().ReverseMap();

⚠️ ReverseMap assumes symmetrical classes. Will silently fail if types drift.


🧨 Nullable Property Mismatch

public string? Name { get; set; } // Source
public string Name { get; set; }  // Destination

πŸ’₯ No compiler error, but potential null reference runtime bugs.


πŸ” Debugging Best Practice

// Don’t map inline in LINQ Select when debugging or tracing
var dtos = products.Select(p => _mapper.Map<ProductDto>(p)); // ❌

var dtos = products.Select(p => p.ToDto()); // βœ…

βœ… Easier to debug, log, and maintain.


πŸ“ Recommended Project Structure

πŸ“¦ src/
┣ πŸ“‚ Domain/
┣ πŸ“‚ Application/
┃ ┣ πŸ“‚ DTOs/
┃ ┣ πŸ“‚ Mappers/
┣ πŸ“‚ Benchmarks/
┣ πŸ“‚ Tests/

βœ… License

MIT License Β© Yaseer Arafat

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment