XForge.AutoMapper.Testing 0.14.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package XForge.AutoMapper.Testing --version 0.14.0
                    
NuGet\Install-Package XForge.AutoMapper.Testing -Version 0.14.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="XForge.AutoMapper.Testing" Version="0.14.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="XForge.AutoMapper.Testing" Version="0.14.0" />
                    
Directory.Packages.props
<PackageReference Include="XForge.AutoMapper.Testing" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add XForge.AutoMapper.Testing --version 0.14.0
                    
#r "nuget: XForge.AutoMapper.Testing, 0.14.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package XForge.AutoMapper.Testing@0.14.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=XForge.AutoMapper.Testing&version=0.14.0
                    
Install as a Cake Addin
#tool nuget:?package=XForge.AutoMapper.Testing&version=0.14.0
                    
Install as a Cake Tool

XForge.AutoMapper

Source-generator-first .NET object mapping library — zero reflection, NativeAOT safe, Blazor WASM friendly.

Build NuGet License: MIT

Modern, fluent, lightweight object mapping library for .NET with source generators, compile-time diagnostics, LINQ/EF Core projection, value converters, hooks, conditional mapping, and NativeAOT compatibility. Ergonomic alternative to AutoMapper, Mapster, and Mapperly.


Why XForge.AutoMapper?

Feature AutoMapper Mapster Mapperly XForge.AutoMapper
Source Generation No Partial Yes Yes (primary)
Zero Reflection (critical path) No Partial Yes Yes
NativeAOT Friendly No Partial Yes Yes
Trimming Safe No Partial Yes Yes
Blazor WASM Slow OK Good First-class
Compile-time Diagnostics No No Yes Yes (XAM prefix)
LINQ Projection Yes Yes Limited Yes
DI Integration Yes Manual Manual Yes (optional)
AutoMapper-style API Yes Yes No Yes ([Map<S,D>])
Reverse Mapping Yes Yes No Yes ([MapReverse])
Value Converters Yes Yes Limited Yes
Conditional Mapping No Yes No Yes ([MapWhen])
Before/After Hooks Yes Yes No Yes
Init-only Properties Yes Yes Yes Yes
Record Support Yes Yes Yes Yes
Enum Mapping Yes Yes Yes Yes
Flattening Yes Yes Yes Yes
Inheritance Yes Yes Yes Yes
Update Existing Yes Yes No Yes
Cold Start Slow Fast Fast Near-zero

Quick Start

1. Install

dotnet add package XForge.AutoMapper
dotnet add package XForge.AutoMapper.SourceGeneration

2. Define your types

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public string Email { get; set; } = "";
    public Address Address { get; set; } = new();
}

public class CustomerDto
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public string Email { get; set; } = "";
    public string AddressStreet { get; set; } = "";  // Auto-flattened
}

3. Create a mapper

Option A: Explicit partial methods (full control)

using XForge.AutoMapper;

[Mapper]
public partial class CustomerMapper
{
    public partial CustomerDto ToDto(Customer source);
}

Option B: AutoMapper-style (class-level declarations)

using XForge.AutoMapper;

[Mapper]
[Map(typeof(Customer), typeof(CustomerDto))]
[Map(typeof(Order), typeof(OrderDto))]
public partial class AppMapper { }

4. Use it

// Explicit partial methods:
var mapper = new CustomerMapper();
var dto = mapper.ToDto(customer);

// AutoMapper-style (non-generic IMapper):
var appMapper = new AppMapper();
CustomerDto dto2 = appMapper.Map<CustomerDto>(customer);

// Via strongly-typed interface:
IMapper<Customer, CustomerDto> iMapper = appMapper;
CustomerDto dto3 = iMapper.Map(customer);

// Update existing instance:
var existingDto = new CustomerDto();
mapper.ToDto(customer, existingDto);

Features

Property Mapping

Properties with the same name and compatible types are mapped automatically:

[Mapper]
public partial class OrderMapper
{
    public partial OrderDto ToDto(Order source);
}

AutoMapper-style API ([Map<S,D>])

Declare mappings at the class level — no partial methods needed:

[Mapper]
[Map(typeof(Customer), typeof(CustomerDto))]
[Map(typeof(Order), typeof(OrderDto))]
public partial class AppMapper { }

// Usage:
var mapper = new AppMapper();
var dto = mapper.Map<CustomerDto>(customer);

The non-generic IMapper interface uses source-generated pattern matching dispatch — zero reflection, AOT-safe.

Custom Property Mappings on [Map]

Use class-level [MapProperty] when property names differ:

[Mapper]
[Map(typeof(Customer), typeof(CustomerDto))]
[MapProperty(typeof(Customer), typeof(CustomerDto), "FullName", "Name")]
[MapProperty(typeof(Customer), typeof(CustomerDto), "YearsOld", "Age")]
public partial class CustomerMapper { }
Auto-generated Reverse Mapping

Set GenerateReverse = true to automatically generate a reverse mapping:

[Mapper]
[Map(typeof(Customer), typeof(CustomerDto), GenerateReverse = true)]
public partial class CustomerMapper { }

// Both directions are available:
var mapper = new CustomerMapper();
CustomerDto dto = mapper.MapCustomerToCustomerDto(customer);
Customer entity = mapper.MapCustomerDtoToCustomer(dto);

Auto-generated methods support all property-level attributes: [IgnoreMap], [ValueConverter], [MapWhen], [NullSubstitute], and flattening.

Custom Mapping ([MapProperty])

Map properties with different names:

[Mapper]
public partial class CustomerMapper
{
    [MapProperty(nameof(Customer.FullName), nameof(CustomerDto.Name))]
    [MapProperty(nameof(Customer.Address.City), nameof(CustomerDto.City))]
    public partial CustomerDto ToDto(Customer source);
}

Ignore Properties ([IgnoreMap])

Skip specific destination properties:

public class CustomerDto
{
    public string Name { get; set; } = "";

    [IgnoreMap]
    public string InternalCode { get; set; } = "";
}

Value Converters ([ValueConverter])

Transform values during mapping:

public class ToUpperConverter : IValueConverter<string, string>
{
    public string Convert(string value) => value.ToUpperInvariant();
}

public class CustomerDto
{
    [ValueConverter(typeof(ToUpperConverter))]
    public string Name { get; set; } = "";
}

Conditional Mapping ([MapWhen])

Map a property only when a source property is non-null:

public class CustomerDto
{
    [MapWhen("Email")]
    public string Email { get; set; } = "";
}

Null Substitution ([NullSubstitute])

Provide fallback values for null source properties:

public class CustomerDto
{
    [NullSubstitute("Unknown")]
    public string Name { get; set; } = "";
}

Reverse Mapping ([MapReverse])

Declare bidirectional mappings:

[Mapper]
public partial class CustomerMapper
{
    public partial CustomerDto ToDto(Customer source);

    [MapReverse(nameof(ToDto))]
    public partial Customer ToEntity(CustomerDto source);
}

Before/After Hooks

Add side effects before or after mapping:

[Mapper]
public partial class CustomerMapper
{
    public partial CustomerDto ToDto(Customer source);

    [BeforeMap]
    private void OnBeforeMap(Customer source) { /* audit, logging */ }

    [AfterMap]
    private void OnAfterMap(CustomerDto dest) { /* post-processing */ }
}

Constructor Mapping ([MapConstructor])

Use a specific constructor for destination creation:

public class OrderDto
{
    [MapConstructor]
    public OrderDto(string orderId, decimal total) { ... }

    public string OrderId { get; }
    public decimal Total { get; }
}

Record Support

Records with positional parameters are automatically mapped via constructor:

public record ProductDto(int Id, string Name, decimal Price);

[Mapper]
public partial class ProductMapper
{
    public partial ProductDto ToDto(Product source);
}

Init-only Properties

Init-only properties are set via object initializer syntax:

public class CustomerDto
{
    public string Name { get; init; } = "";
    public int Age { get; set; }
}

Collection Mapping

Collection properties are automatically iterated and mapped:

public class OrderDto
{
    public List<OrderItemDto> Items { get; set; } = new();
}

Enum Mapping

Different enum types are mapped via cast:

public enum SourceStatus { Active, Inactive }
public enum DestStatus { Active, Inactive }

// Generated: destination.Status = (DestStatus)source.Status;

Flattening

Nested properties are auto-flattened using PascalCase prefix matching:

public class Customer
{
    public Address Address { get; set; } = new();
}

public class CustomerDto
{
    public string AddressStreet { get; set; } = ""; // → source.Address.Street
    public string AddressCity { get; set; } = "";   // → source.Address.City
}

Inheritance

Properties from base classes are included automatically:

public class BaseEntity
{
    public int Id { get; set; }
    public DateTime CreatedAt { get; set; }
}

public class Customer : BaseEntity
{
    public string Name { get; set; } = "";
}

// Id, CreatedAt, and Name are all mapped

LINQ Projection (ProjectTo)

Generate expression trees for IQueryable projection:

using XForge.AutoMapper.Queryable;

[Mapper]
public partial class CustomerMapper
{
    public partial CustomerDto ToDto(Customer source);
}

// Usage with EF Core:
var dtos = dbContext.Customers
    .Where(c => c.IsActive)
    .Select(CustomerMapper.ToDtoExpression)
    .ToListAsync();

Dependency Injection

Register mappers in IServiceCollection:

dotnet add package XForge.AutoMapper.Extensions.DependencyInjection
using XForge.AutoMapper.Extensions.DependencyInjection;

services.AddXForgeAutoMapper(typeof(CustomerMapper).Assembly);

// Resolve via DI:
var mapper = serviceProvider.GetRequiredService<IMapper<Customer, CustomerDto>>();

// Non-generic IMapper (for AutoMapper-style usage):
var iMapper = serviceProvider.GetRequiredService<IMapper>();
var dto = iMapper.Map<CustomerDto>(customer);

Testing Helpers

dotnet add package XForge.AutoMapper.Testing
using XForge.AutoMapper.Testing;

var mapper = new CustomerMapper();
var source = new Customer { Name = "Test" };

// Assert all matching properties are correctly mapped:
MapperAssert.AssertMapping(mapper, source);

// Assert with ignored properties:
MapperAssert.AssertMapping(mapper, source, "ComputedField");

Update Existing Destination

The generator emits an overload that updates an existing instance:

var existing = new CustomerDto { Age = 0 };
mapper.ToDto(customer, existing); // Updates existing instance, skips init-only props

Diagnostics

XForge.AutoMapper emits compile-time diagnostics with the XAM prefix:

ID Severity Description
XAM001 Warning Target property has no matching source property
XAM002 Info Source property is not mapped to any target property
XAM003 Warning Cyclic mapping detected (A→B and B→A in same mapper)
XAM004 Error Invalid value converter type
XAM005 Error Ambiguous mapping hook (MapMethodName required)
XAM006 Error Invalid [MapWhen] source property
XAM007 Error Invalid [MapReverse] method reference
XAM008 Error [Map] attribute conflicts with existing partial method
XAM010 Error [Mapper] class must be partial
XAM011 Error Mapper method must be partial
XAM012 Warning Duplicate [MapProperty] target

Architecture

XForge.AutoMapper.Abstractions     → Contracts & attributes (netstandard2.0)
XForge.AutoMapper                  → Minimal runtime (net8.0/9.0/10.0)
XForge.AutoMapper.SourceGeneration → Roslyn incremental source generator
XForge.AutoMapper.Analyzers        → Roslyn analyzers & code fixes
XForge.AutoMapper.Queryable        → IQueryable projection support
XForge.AutoMapper.Extensions.DependencyInjection → IServiceCollection integration
XForge.AutoMapper.Testing          → Testing helpers
XForge.AutoMapper.Benchmarks       → BenchmarkDotNet performance suite

Benchmarks

Run benchmarks locally:

cd benchmarks/XForge.AutoMapper.Benchmarks
dotnet run -c Release -- --filter "*"

Available benchmarks:

  • SimpleClassBenchmark — Simple flat class mapping
  • NestedObjectBenchmark — Nested object mapping
  • CollectionBenchmark — Collection (50 items) mapping
  • RecordBenchmark — Record constructor mapping
  • EnumBenchmark — Enum type mapping
  • FlatteningBenchmark — Nested-to-flat property mapping
  • InheritanceBenchmark — Base class property mapping
  • UpdateDestinationBenchmark — Update existing instance
  • ColdStartBenchmark — First-run overhead comparison

Requirements

  • .NET 8.0, 9.0, or 10.0
  • C# 12+

License

MIT

Contributing

See CONTRIBUTING.md for development setup and guidelines.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0-preview.1 32 5/21/2026
0.23.0 33 5/22/2026
0.22.0 33 5/22/2026
0.20.0 41 5/22/2026
0.19.0 41 5/22/2026
0.18.0 36 5/21/2026
0.17.0 35 5/21/2026
0.16.0 34 5/21/2026
0.15.0 34 5/21/2026
0.14.0 34 5/21/2026