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);
}
[Map(typeof(OrderDto), GenerateProjection = true)]
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: an Expression<Func<Order, OrderDto>> plus a
// ProjectToOrderDto() IQueryable extension — translated by EF Core,
// so only the mapped columns are selected in SQL.
var dtos = dbContext.Orders
.Where(o => o.Total > 100)
.ProjectToOrderDto()
.ToList();
No new project, no clone required — paste this into a single .cs file
and run it with the .NET SDK's file-based apps (.NET 10+), or scaffold the full demo
with dotnet new.
// Save as automap-try.cs, then run: dotnet run automap-try.cs
#:package AutoMap.Generator@1.*
using AutoMap;
[Map(typeof(OrderDto))]
public class Order
{
public int Id { get; set; }
public string Customer { get; set; } = "";
}
public class OrderDto
{
public int Id { get; set; }
public string Customer { get; set; } = "";
}
var order = new Order { Id = 1, Customer = "Ada" };
Console.WriteLine(order.ToOrderDto().Customer); // "Ada"
dotnet new install AutoMap.Generator.Templates
dotnet new automap-demo -o MyAutoMapDemo
cd MyAutoMapDemo
dotnet run
Scaffolds a console app exercising every attribute — flattening, enums, reverse mapping, projections, and more. See templates/ for source.
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 |
| AM007 | ⚠ Warning | Strict mode: unmapped destination property |
| AM008 | ⚠ Warning | [GenerateProjection] requested but mapping uses constructs unsupported in expression trees (e.g. ?., switch) — projection skipped |
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