YounMapper 1.1.2

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

YounMapper

A small, fast, convention-based object-to-object mapper for .NET — profiles, property renames, flattening, custom type converters, and collection mapping. No dependency injection container required.

The mapping semantics are defined in a language-neutral contract (SEMANTICS.md) so the library can be ported to TypeScript, Python, and other languages with identical behavior.

Quickstart

Install the package:

dotnet add package YounMapper

Three lines to a mapped object:

using YounMapper;

var mapper = new MapperConfiguration(cfg => cfg.AddProfile<MyProfile>())
    .CreateMapper();
var dto = mapper.Map<OrderDto>(order); // convention mapping, zero config

With a profile for the non-conventional parts:

public class MyProfile : Profile
{
    public MyProfile()
    {
        CreateMap<Order, OrderDto>()
            .ForMember(dest => dest.CustomerName,
                       opt => opt.MapFrom(src => src.Customer.Name));
    }
}

No container is required — MapperConfiguration and IMapper work standalone. Mapper instances are immutable and reusable. When resolvers use scoped services, create a mapper within each service scope rather than capturing those services in an application-wide singleton.

Features

Feature How Example
Convention-based mapping Matching names (case-insensitive) with assignable types map automatically src.Name → dest.Name
Mapping profiles Profile + CreateMap<T1, T2>(), last registration wins cfg.AddProfile<MyProfile>()
Property renames ForMember(...).MapFrom(...) dest.CustomerName ← src.Customer.Name
Ignore Preserve initialized or existing values opt.Ignore()
Reverse maps Reverse property paths and flattening .ReverseMap()
Custom construction Create a destination before mapping members .ConstructUsing(src => new Dto(src.Id))
Member resolvers Typed resolver or supplied instance opt.MapFrom<AssetPolicyResolver>()
Existing destinations Populate an existing object mapper.Map(source, destination)
Runtime types Map with resolved type descriptors mapper.Map(source, typeof(A), typeof(B))
Flattening Automatic, null-safe, greedy prefix decomposition src.Customer.Address.City → dest.CustomerAddressCity
Custom type converters ITypeConverter<TSource, TDestination> or a Func delegate CreateMap<A, B>().ConvertUsing(a => new B(...))
Collections Element-wise mapping for IEnumerable<T>/List<T>/T[] List<Order> → List<OrderDto>
Null handling Null source → null destination; null collections → null (or empty via one global switch) cfg.MapNullCollectionsToEmpty()

AutoMapper-style APIs (1.1.x)

The familiar syntax below is supported with zero runtime package dependencies. YounMapper is not a full or binary-compatible replacement for AutoMapper: use using YounMapper; for mapping configuration. Resolvers may implement YounMapper's interface or expose a compatible public Resolve method (since 1.1.1).

using YounMapper;

IMapper mapper = new MapperConfiguration(cfg => cfg.AddProfile<AssetProfile>()).CreateMapper();
var asset = new Asset { Id = 7, Name = "Router", IsRestricted = true };
var dto = mapper.Map<AssetDto>(asset);
var existing = new AssetDto(7) { AuditNote = "Keep this" };
mapper.Map(asset, existing);
var restored = mapper.Map<Asset>(dto);
var runtimeDto = (AssetDto)mapper.Map(asset, typeof(Asset), typeof(AssetDto))!;

public sealed class AssetProfile : Profile
{
    public AssetProfile()
    {
        CreateMap<Asset, AssetDto>()
            .ConstructUsing(src => new AssetDto(src.Id))
            .ForMember(dest => dest.DisplayName, opt => opt.MapFrom(src => src.Name))
            .ForMember(dest => dest.AuditNote, opt => opt.Ignore())
            .ForMember(dest => dest.Policy, opt => opt.MapFrom<AssetPolicyResolver>())
            .ReverseMap();
    }
}

public sealed class AssetPolicyResolver : IValueResolver<Asset, AssetDto, string>
{
    public string Resolve(Asset source, AssetDto destination, string destMember, ResolutionContext context)
        => source.IsRestricted ? "Restricted" : "Standard";
}

public sealed class Asset
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public bool IsRestricted { get; set; }
}

public sealed class AssetDto
{
    public AssetDto(int id) => Id = id;
    public int Id { get; }
    public string? DisplayName { get; set; }
    public string? AuditNote { get; set; }
    public string Policy { get; set; } = "";
}
  • Ignore() preserves initialized/existing values and never reads the source member.
  • ConstructUsing() runs before member mapping; it is skipped for existing destinations.
  • MapFrom<TResolver>() validates the signature during registration and constructs one reusable resolver per member rule when creating each mapper. From 1.1.2, constructor dependencies are supported through cfg.ConstructServicesUsing(...); without a factory, the resolver needs a public parameterless constructor. Supplied instances bypass the factory. YounMapper's context.Mapper allows nested mapping; resolver state must be thread-safe.
  • From 1.1.1, existing resolvers do not have to implement YounMapper.IValueResolver. Keep opt.MapFrom<AssetPolicyResolver>(): a public instance Resolve(source, destination, destMember) or Resolve(source, destination, destMember, context) is bound once. Parameter/result types must match the mapping; ambiguous or unsupported signatures fail during configuration. Native YounMapper resolvers take precedence. Important: a foreign context such as AutoMapper.ResolutionContext receives null. Existing AutoMapper resolvers work only when they do not use that context; its Mapper, Items, State, and service-resolution features are not emulated. Keep AutoMapper referenced in your application while its types remain in your resolver declarations; YounMapper itself still has no runtime package dependencies.
  • ReverseMap() reverses public property paths, renames, and implicit flattening (unflattening creates missing public nested objects). Factory/resolver/converter logic, Ignore, and computed expressions are directional, not inverted. It is not a lossless round trip; configure any additional reverse rules after the call.
  • Map(source, existing) returns the populated instance. Nested members may be replaced, not merged. A null source leaves it unchanged; a null destination creates a new one. Whole converters and top-level collection maps may return replacements.
  • Runtime-type forms are Map(source, sourceType, destinationType) and Map(source, destination, sourceType, destinationType).

Resolvers with constructor dependencies (1.1.2)

Keep .ForMember(d => d.UploadedBy, o => o.MapFrom<EmployeeEmailResolver>()). Configure ConstructServicesUsing to construct the resolver with your service. For an ASP.NET Core application with IInfoMappingService already registered:

using Microsoft.Extensions.DependencyInjection;

builder.Services.AddScoped<EmployeeEmailResolver>();
builder.Services.AddScoped<YounMapper.IMapper>(sp =>
    new YounMapper.MapperConfiguration(cfg =>
    {
        cfg.AddProfile<MappingProfile>();
        cfg.ConstructServicesUsing(sp.GetRequiredService);
    }).CreateMapper());

Register every resolver used by MapFrom<TResolver>() with the service provider when using GetRequiredService, including parameterless resolvers. Keep your existing IInfoMappingService implementation/registration. Do not build another service provider or register a singleton mapper that captures scoped dependencies. Resolvers and their dependencies are owned/disposed by the application's scope, not YounMapper. This callback also supports compatible legacy resolvers; foreign context parameters still receive null.

Without a container, supply your own factory, for example cfg.ConstructServicesUsing(type => type == typeof(EmployeeEmailResolver) ? new EmployeeEmailResolver(infoMappingService) : throw new InvalidOperationException($"Unregistered resolver: {type}")); or pass an already-created resolver with opt.MapFrom(instance).

Query projection, DI integration packages, validation APIs, general ForPath configuration, and automatic reversal of arbitrary business logic are not implemented.

Design guarantees

  • Compiled, cached plans — reflection runs once per type pair; the hot path is a compiled delegate with zero reflection (see benchmarks).
  • Public members only — private/internal state is never read or written.
  • No code generated from untrusted data — trusted expression trees are compiled and cached. Nothing is parsed from strings or input data. Callbacks and property accessors are application code, not sandboxed. See SECURITY.md.
  • Fail fast — invalid member rules and type mismatches throw at mapper creation, not at map time.

Benchmarks

dotnet run -c Release --project benchmarks/YounMapper.Benchmarks

A convention-only map of a flat 10-property pair completes in well under 1 µs per call after warmup.

Roadmap / ports

  • v1.x — C#/.NET only; the feature set defined in SEMANTICS.md.
  • TypeScript — planned port following the same semantics contract.
  • Python — planned port following the same semantics contract.
  • Later — query projection, validation, DI-container integration packages. Explicitly not in v1.

Contributing

See SECURITY.md for the security review checklist and RELEASING.md for the release process. Tests and coverage:

dotnet test
node tools/coverage.js --min-line 90 --min-branch 90   # after running tests with coverlet

License

MIT — see LICENSE.

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 was computed.  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 was computed.  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.
  • net8.0

    • No dependencies.

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.1.2 102 10/29/2026
1.1.1 115 10/29/2026 1.1.1 is deprecated because it has critical bugs.
1.1.0 109 10/28/2026 1.1.0 is deprecated because it has critical bugs.
1.0.0 118 10/28/2026 1.0.0 is deprecated because it is no longer maintained.