Minicon.CodeMapper 0.2.2

dotnet add package Minicon.CodeMapper --version 0.2.2
                    
NuGet\Install-Package Minicon.CodeMapper -Version 0.2.2
                    
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="Minicon.CodeMapper" Version="0.2.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Minicon.CodeMapper" Version="0.2.2" />
                    
Directory.Packages.props
<PackageReference Include="Minicon.CodeMapper" />
                    
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 Minicon.CodeMapper --version 0.2.2
                    
#r "nuget: Minicon.CodeMapper, 0.2.2"
                    
#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 Minicon.CodeMapper@0.2.2
                    
#: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=Minicon.CodeMapper&version=0.2.2
                    
Install as a Cake Addin
#tool nuget:?package=Minicon.CodeMapper&version=0.2.2
                    
Install as a Cake Tool

Minicon.CodeMapper

Schlanker Objekt-zu-Objekt-Mapper für .NET auf Basis eines Source Generators – als Ablösung für AutoMapper. Die vertraute Profile / CreateMap-DSL bleibt erhalten, die Mappings werden jedoch zur Compile-Zeit als statischer, reflection-freier Code erzeugt.

  • Schnell – keine Reflection im heißen Pfad, nur direkte Zuweisungen und Delegate-Lookups
  • 🧩 Vertraute APIProfile, CreateMap<,>(), ForMember, MapFrom, Ignore, ReverseMap
  • 🔍 Compile-Zeit-Diagnostics – nicht zuordenbare Ziel-Member werden direkt im Build gemeldet
  • 🪶 Trimming-/AOT-freundlich – der generierte Mapping-Code ist vollständig statisch
  • 🆓 MIT-Lizenz – keine kommerzielle Lizenzpflicht

Status: v0.1 – die Kern-Features stehen und sind getestet. Siehe Roadmap.


Installation

Projektreferenz (während der Entwicklung):

<ProjectReference Include="path/to/src/Minicon.CodeMapper/Minicon.CodeMapper.csproj" />

Der zugehörige Source Generator ist im Paket enthalten und wird automatisch aktiv.

Schnellstart

1. Profile definieren – exakt wie gewohnt:

using Minicon.CodeMapper;

public class PersonProfile : Profile
{
    public PersonProfile()
    {
        CreateMap<Address, AddressDto>().ReverseMap();
        CreateMap<Order, OrderDto>();
        CreateMap<Person, PersonDto>()
            .ForMember(d => d.FullName, o => o.MapFrom(s => s.FirstName + " " + s.LastName))
            .ForMember(d => d.Secret,   o => o.Ignore());
    }
}

2. Registrieren (Microsoft.Extensions.DependencyInjection):

services.AddCodeMapper(typeof(PersonProfile).Assembly);

3. Mappen:

public class PersonService(IMapper mapper)
{
    public PersonDto ToDto(Person p) => mapper.Map<PersonDto>(p);
    public List<PersonDto> ToDtos(IEnumerable<Person> people) => mapper.Map<List<PersonDto>>(people);
}

Ohne DI geht es auch statisch über Mapper.Instance:

var dto = Mapper.Instance.Map<PersonDto>(person);

Unterstützte Features (v0.1)

Feature Status Hinweis
CreateMap<TSource, TDestination>() Property-/Feld-Zuordnung nach Name
ForMember(..., o => o.MapFrom(s => expr)) beliebiger Quell-Ausdruck
ForMember(..., o => o.Ignore())
ReverseMap() erzeugt Auto-Mapping in Gegenrichtung (nicht in Kombination mit ConvertUsing)
Verschachtelte Objekte nutzt registriertes Sub-Mapping
Collections (List<T>, T[], IEnumerable<T>, …) Element-Mapping
Nullable<T>T via GetValueOrDefault()
Implizite Konvertierungen z. B. intlong
Enum-Konvertierungen Enum ↔ Zahl, Enum ↔ Enum, string ↔ Enum (inkl. Nullable<TEnum>)
ConvertUsing(...) vollständig benutzerdefinierte Konvertierung
ConstructUsing(...) benutzerdefinierte Erzeugung, übrige Member werden weiter gemappt
Ziele ohne parameterlosen Konstruktor passender Konstruktor wird automatisch gewählt
record / init-only Properties primärer Konstruktor + Objekt-Initializer
BeforeMap / AfterMap 🚧 DSL vorhanden, noch ohne Codegen
ValueResolver / Condition / NullSubstitute 🚧 geplant

Konstruktoren

Hat der Zieltyp einen zugänglichen parameterlosen Konstruktor, wird wie gewohnt new Dest { ... } erzeugt. Andernfalls wählt der Generator den Konstruktor mit den meisten Parametern, die sich vollständig aus der Quelle befüllen lassen – Parameternamen werden dabei case-insensitiv gegen Quell-Member und ForMember-Konfigurationen aufgelöst, optionale Parameter dürfen offenbleiben:

public class Process
{
    public Process(string name, int priority = 5) { Name = name; Priority = priority; }
    public string Name { get; }
    public int Priority { get; }
    public List<ProcessParameter> Parameters { get; set; } = new();
}

// generiert:
var dest = new Process(name: source.Name, priority: source.Priority)
{
    Parameters = mapper.Map<List<ProcessParameter>>(source.Parameters),
};

Bereits per Konstruktor befüllte Member werden im Objekt-Initializer nicht erneut gesetzt.

Passt kein Konstruktor – etwa weil die Parameter aus verschachtelten Quellen stammen –, gibt ConstructUsing die Erzeugung vor. Anders als ConvertUsing ersetzt es nur die Konstruktion; alle übrigen Member werden danach weiterhin automatisch gemappt:

CreateMap<ProcessEntity, string>()
    .ConstructUsing(e => e.Name);

CreateMap<ProcessParameterEntity, ProcessParameter>()
    .ConstructUsing(e => new ProcessParameter(
        e.Process.Name,
        e.Name,
        e.Value,
        Enum.Parse<ParameterSourceType>(e.ParameterSource.Name, true)));

init-only Properties lassen sich nach ConstructUsing nicht mehr setzen und gehören deshalb in den Konstruktor-Ausdruck.

Enums

Enum-Werte werden ohne zusätzliche Konfiguration konvertiert – sowohl für Member als auch für Konstruktor-Parameter:

Quelle Ziel Erzeugter Code
int (o. a. Zahlentyp) TEnum (TEnum)source.X
TEnum int (int)source.X
TEnumA TEnumB (TEnumB)source.X
string TEnum Enum.Parse<TEnum>(source.X, true) (case-insensitiv)
TEnum string source.X.ToString()
int? / TEnum? TEnum? gehobener Cast – null bleibt null
string TEnum? null bei leerem/whitespace-Wert

Nullable-Quellen auf nicht-nullable Ziele laufen wie bisher über GetValueOrDefault() (int?TEnum ergibt bei null also den Enum-Wert 0). Benutzerdefinierte explicit operator-Konvertierungen werden ebenfalls genutzt.

Diagnosen

ID Severity Bedeutung
MINI001 Info Ziel-Member konnte keiner Quelle zugeordnet werden und bleibt auf dem Standardwert.
MINI002 Error Kein zugänglicher Konstruktor des Zieltyps ist aus der Quelle befüllbar – fehlende Werte per ForMember(...MapFrom(...)) ergänzen oder die Erzeugung per ConstructUsing(...) vorgeben.
MINI003 Error Zieltyp ist kein konstruierbarer Objekttyp (string, Enum, Primitive, Interface, abstrakte Klasse) – hierfür ConstructUsing(...) oder ConvertUsing(...) verwenden.

Migration von AutoMapper

In den meisten Fällen genügt das Ersetzen des Namespaces und der DI-Methode:

AutoMapper Minicon.CodeMapper
using AutoMapper; using Minicon.CodeMapper;
class X : Profile class X : Profile (unverändert)
CreateMap<A, B>()... unverändert
IMapper / mapper.Map<T>(x) unverändert
services.AddAutoMapper(asm) services.AddCodeMapper(asm)

Wie es funktioniert

  1. Der Source Generator findet alle Profile-Subklassen und liest deren CreateMap/ForMember-Deklarationen aus dem Syntaxbaum.
  2. Pro Typ-Paar wird eine statische Methode erzeugt (new TDest { ... } mit direkten Zuweisungen).
  3. Alle Methoden werden per [ModuleInitializer] in der MapperRegistry registriert – ganz ohne Assembly-Scan zur Laufzeit.
  4. IMapper.Map<T>() ist nur noch ein Dictionary-Lookup auf den passenden Delegate.

Auszug aus generiertem Code:

internal static PersonDto Map_3(Person source, IMapper mapper)
{
    if (source is null) return default!;
    return new PersonDto
    {
        Id        = source.Id,
        FullName  = ((Func<Person, string>)(s => s.FirstName + " " + s.LastName))(source),
        LuckyNumber = source.LuckyNumber.GetValueOrDefault(),
        HomeAddress = mapper.Map<AddressDto>(source.HomeAddress),
        Orders      = mapper.Map<List<OrderDto>>(source.Orders),
        // Secret wird ignoriert
    };
}

Trimming & AOT

Der generierte Mapping-Code ist vollständig statisch typisiert und reflection-frei. Einzige Ausnahme ist ein Komfort-Fallback in Mapper für Top-Level-Collection-Aufrufe wie Map<List<T>>(...), der minimale Reflection nutzt (sauber annotiert). Für NativeAOT empfiehlt es sich, registrierte Element-Mappings direkt zu verwenden; die automatische Generierung dedizierter Collection-Maps ist auf der Roadmap.

Roadmap

  • Inline-Codegen für Collections (vollständige AOT-Reinheit ohne Reflection-Fallback)
  • BeforeMap / AfterMap im Codegen
  • Condition, NullSubstitute, ValueResolver
  • Flattening (Order.Customer.NameOrderDto.CustomerName)
  • NuGet-Veröffentlichung
  • Drop-in-Kompatibilitätspaket (Namespace-Alias AutoMapper)

Build & Test

dotnet build
dotnet test

Lizenz

MIT © Michael Nikolaus

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
0.2.2 114 8/24/2026
0.2.1 97 8/24/2026
0.2.0 103 8/24/2026
0.1.1 139 6/25/2026
0.1.0 119 6/25/2026