Add [Map(typeof(OrderDto))]
to your domain class and get a generated ToOrderDto()
extension method — zero reflection, AOT-safe, and ready at build time.
dotnet add package AutoMap.Generator
using AutoMap;
[Map(typeof(OrderDto))]
public class Order
{
public int Id { get; set; }
public string CustomerName { get; set; } = "";
public decimal Total { get; set; }
}
public static class AutoMapExtensions
{
public static OrderDto ToOrderDto(this Order source)
=> new()
{
Id = source.Id,
CustomerName = source.CustomerName,
Total = source.Total,
};
}
Convention-first mapping with generated extensions, collection helpers, and Roslyn diagnostics.
Mapping code is emitted at compile time, not discovered at runtime. Plain generated C# with no reflection or expression compilation.
Works with .NET Native AOT and Blazor WebAssembly. No runtime scanning to break trimming or ahead-of-time compilation.
Properties are matched by name automatically, so the common case stays zero-config and easy to read.
When nested source types also have [Map], AutoMap chains the generated calls for you automatically.
Generates companion ToXDtos(IEnumerable<X>) helpers so single-object and collection mapping stay consistent.
Type mismatch? The IDE lightbulb can add [MapIgnore] for you when a property should be skipped.
Build-time mapping you can read, debug, and navigate in the IDE.
using AutoMap;
[Map(typeof(OrderDto))]
public class Order
{
public int Id { get; set; }
public string CustomerName { get; set; } = "";
public decimal Total { get; set; }
}
public class OrderDto
{
public int Id { get; set; }
public string CustomerName { get; set; } = "";
public decimal Total { get; set; }
}
// Generated — use it like this:
var dto = order.ToOrderDto();
var dtos = orders.ToOrderDtos(); // collection helper
[Map(typeof(AddressDto))]
public class Address
{
public string Street { get; set; } = "";
public string City { get; set; } = "";
}
[Map(typeof(CustomerDto))]
public class Customer
{
public string Name { get; set; } = "";
public Address HomeAddress { get; set; } = new();
}
// AutoMap chains them automatically:
var dto = customer.ToCustomerDto();
// dto.HomeAddress is AddressDto — generated null-safe call
[Map(typeof(ProductDto))]
public class Product
{
public int Id { get; set; }
public string InternalCode { get; set; } = ""; // rename
[MapProperty("InternalCode")] // map to different source property
public string Sku { get; set; } = "";
[MapIgnore] // skip this property
public string Secret { get; set; } = "";
[MapWith("source.Price * 1.2m")] // custom expression
public decimal PriceWithVat { get; set; }
}
// AutoMap generates a mapper class you can inject:
services.AddSingleton<IAutoMapper<Order, OrderDto>>(
AutoMapExtensions.OrderToOrderDtoMapper.Instance);
// Inject and use:
public class OrderService(IAutoMapper<Order, OrderDto> mapper)
{
public OrderDto GetDto(Order order) => mapper.Map(order);
}
Problems surfaced during compilation — before they become runtime surprises.
| Code | Severity | Description |
|---|---|---|
| AM001 | ⛔ Error | Destination type not found |
| AM002 | ⛔ Error | Destination has no settable properties matching source |
| AM003 | ⚠ Warning | Property type mismatch — consider [MapWith] |
| AM004 | ⚠ Warning | Property skipped due to incompatible type — use [MapIgnore] or add [Map] on source type. IDE lightbulb available. |
| AM005 | ⛔ Error | [MapWith] expression cannot be blank |
| AM006 | ⚠ Warning | Circular mapping detected |
Designed for compile-time mapping, runtime simplicity, and AOT-friendly applications.
| Feature | AutoMap.Generator | AutoMapper | Mapster |
|---|---|---|---|
| Compile-time generated | ✅ | ❌ | ❌ |
| Zero reflection | ✅ | ❌ | ❌ |
| AOT / NativeAOT | ✅ | ❌ | Partial |
| IDE navigation to mapping code | ✅ | ❌ | ❌ |
| Roslyn diagnostics | ✅ | ❌ | ❌ |
| Runtime config | ❌ | ✅ | ✅ |
| Projection (IQueryable) | ❌ | ✅ | ✅ |
Also by the same author