Mapify.NET
2.3.2
See the version list below for details.
dotnet add package Mapify.NET --version 2.3.2
NuGet\Install-Package Mapify.NET -Version 2.3.2
<PackageReference Include="Mapify.NET" Version="2.3.2" />
<PackageVersion Include="Mapify.NET" Version="2.3.2" />
<PackageReference Include="Mapify.NET" />
paket add Mapify.NET --version 2.3.2
#r "nuget: Mapify.NET, 2.3.2"
#:package Mapify.NET@2.3.2
#addin nuget:?package=Mapify.NET&version=2.3.2
#tool nuget:?package=Mapify.NET&version=2.3.2
Mapify.NET
Mapify is a lightweight .NET library for creating static mapping expressions for C# objects. It bridges the gap between in-memory object mapping and LINQ projections (e.g., Entity Framework), allowing you to reuse the same mapping logic consistently across your application.
Features โจ
- Zero Boilerplate: Automatically maps properties with compatible names and types.
- Entity Framework Compatible: Generates expression trees compatible with
IQueryableprojections. - Safe: Handles null checks and type conversions automatically.
- Flexible: Supports explicit overrides and partial mappings.
- Performance: Caches compiled delegates for in-memory mapping.
Installation ๐ฆ
Install via NuGet:
dotnet add package Mapify.NET
Supported Frameworks ๐
- .NET 8.0, 9.0, 10.0
- .NET Standard 2.0, 2.1
- .NET Framework 4.6.2+
Getting Started ๐
1. Define your Classes
public class Address {
public string Street { get; set; }
public string City { get; set; }
}
public class AddressDto {
public string Street { get; set; }
public string City { get; set; }
}
public class Person {
public string FirstName { get; set; }
public string LastName { get; set; }
public Address MainAddress { get; set; }
public ICollection<Address> Addresses { get; set; }
}
public class PersonDto {
public string Name { get; set; }
public AddressDto MainAddress { get; set; }
public ICollection<AddressDto> Addresses { get; set; }
}
2. Create Profiles (recommended)
The recommended approach is profile + instance mapper (IMapify).
It keeps mapping configuration explicit and is easier to test.
using Mapify.NET;
using Microsoft.Extensions.DependencyInjection;
public class PersonProfile : MapifyProfile {
protected override void Configure() {
CreateMap<Person, PersonDto>();
}
}
// Use UseMap<TSource, TTarget>(sourceMember) to explicitly mark that
// a property should be mapped via an existing registered map.
public class QueryProfile : MapifyProfile {
protected override void Configure() {
CreateMap<Address, AddressDto>();
CreateMap<Person, PersonDto>(p => new PersonDto {
Name = p.FirstName + " " + p.LastName,
MainAddress = UseMap<Address, AddressDto>(p.MainAddress)
});
}
}
// Named maps: same source/target pair, different map names.
public class PersonValueProfile : MapifyProfile {
protected override void Configure() {
// default map for Person -> string
CreateMap<Person, string>(x => x.FirstName);
// named maps for the same Person -> string type pair
CreateMap<Person, string>("FullName", x => x.FirstName + " " + x.LastName);
CreateMap<Person, string>("Initials", x => x.FirstName.Substring(0, 1) + x.LastName.Substring(0, 1));
}
}
// UseMap also supports named maps in profile initializers.
public class StudentProfile : MapifyProfile {
protected override void Configure() {
CreateMap<Student, StudentDto>("Raw", x => new StudentDto { Name = x.Name });
CreateMap<Student, StudentDto>("Upper", x => new StudentDto { Name = x.Name.ToUpper() });
CreateMap<Classroom, ClassroomDto>(x => new ClassroomDto {
Students = UseMap<IEnumerable<Student>, IEnumerable<StudentDto>>("Upper", x.Students)
});
}
}
// If source and destination property names differ, pass the source explicitly.
public class NumberProfile : MapifyProfile {
protected override void Configure() {
CreateMap<NumberSource, NumberDto>();
CreateMap<Order, OrderDto>(x => new OrderDto {
// Order.SourceNumber -> OrderDto.Number
Number = UseMap<NumberSource, NumberDto>(x.SourceNumber)
});
}
}
var services = new ServiceCollection();
// Scans the given assemblies for all IMapifyProfile implementations,
// registers them, and registers IMapify as singleton.
services.AddMapify(typeof(PersonProfile).Assembly);
// Scans the given assemblies for all IMapifyProfile implementations,
// registers them, and registers IMapify as Scoped.
services.AddMapify(ServiceLifetime.Scoped, typeof(PersonProfile).Assembly);
// Manually add profiles of the given assembly
// registers IMapify as Scoped without adding profiles
services.AddMapifyProfiles(typeof(PersonProfile).Assembly);
services.AddMapify(ServiceLifetime.Scoped);
// Register a specific profile manually
services.AddMapifyProfile<PersonProfile>();
services.AddMapify(ServiceLifetime.Scoped);
// Named mapper with isolated profile set
services.AddMapifyProfile<PersonProfile>("queries");
services.AddMapifyNamed("queries", ServiceLifetime.Transient);
var provider = services.BuildServiceProvider();
var defaultMapper = provider.GetRequiredService<IMapify>();
var queryMapper = provider.GetMapify("queries");
CreateMap<TSource, TTarget>(...) inside MapifyProfile is registration-only.
Map building is deferred until all profiles are registered, so unordered registrations are supported.
When you need explicit nested map usage in a profile initializer, use UseMap<TSource, TTarget>(x.SourceMember).
During build, Mapify resolves the dependency to the registered map (including nullable variants).
To force a specific named map, use UseMap<TSource, TTarget>("Name", x.SourceMember).
To explicitly ignore a destination property in a partial map, use Ignore<T>():
CreateMap<Person, PersonDto>(x => new PersonDto {
Name = x.FirstName + " " + x.LastName,
InternalCode = Ignore<string>()
});
Ignore behavior:
- For new object mapping (
Map(source)/ projections), ignored properties are not mapped from source. - If an ignored destination property is marked
required:- Mapify preserves an existing class/constructor initializer when present.
- Otherwise, Mapify emits the configured fallback (
default(T)for non-collections, empty fallback for non-nullable collections).
- For map-to-existing (
Map(source, existing)), ignored properties are left unchanged on the target instance.
You can also chain LINQ operators after UseMap, for example:
CreateMap<Person, PersonDto>(x => new PersonDto {
Addresses = UseMap<IEnumerable<Address>, IEnumerable<AddressDto>>(x.Addresses)
.OrderBy(dto => dto.StreetName)
});
UseMap also supports arrays and enumerable types. If a map exists for element types (TSrc -> TDest),
you can use it for collection shapes like TSrc[] -> TDest[] and IEnumerable<TSrc> -> IEnumerable<TDest>.
This also applies to named maps: if UseMap is called with a name for a collection shape,
Mapify resolves the named map for the individual element types.
CreateMap<Address, AddressDto>("Postal", x => new AddressDto {
StreetName = x.StreetName
});
CreateMap<Person, PersonDto>(x => new PersonDto {
Addresses = UseMap<IEnumerable<Address>, IEnumerable<AddressDto>>("Postal", x.Addresses)
.OrderBy(dto => dto.StreetName)
});
In this example, Mapify applies the named element map Address -> AddressDto with name "Postal" for each item.
Recursive UseMap depth
For self-referencing or cyclic object graphs, Mapify expands recursive UseMap calls to a finite depth.
- If no depth is specified, the default recursion depth is
6. - You can override depth per marker with:
UseMap<TSource, TTarget>(source, maxDepth)UseMap<TSource, TTarget>("Name", source, maxDepth)
maxDepthmust be a constant positive integer in the expression.
Mapify also enforces a hard safety cap during map build:
- Default hard cap:
10 - Configure per mapper instance:
mapify.UseMaxRecursiveMapBuildDepth(value) - If any marker depth exceeds the hard cap, map building throws
InvalidOperationException.
ProjectTo<TTarget>() markers inside CreateMap expressions are rewritten to the same internal behavior, so the same recursive depth rules apply.
3. Use the instance mapper in-memory
var mapper = provider.GetRequiredService<IMapify>();
var dto = mapper.Map<Person, PersonDto>(person);
// named map execution
var fullName = mapper.Map<Person, string>(person, "FullName");
var existing = new PersonDto();
mapper.Map(person, existing);
// named map-to-existing execution
mapper.Map(person, existing, "SomeNamedMap");
4. Use the instance mapper with Entity Framework (IQueryable)
IMapify.GetMap<TSource, TTarget>() returns the expression for projections (or null when missing). Use IMapify.GetRequiredMap<TSource, TTarget>() when a missing map should throw.
To project with EF/EF Core using instance-based profiles, pass the mapper to ProjectTo<TTarget>():
var people = await _dbContext.Persons
.ProjectTo<PersonDto>(_mapify)
.OrderBy(x => x.Name)
.ToArrayAsync(cancellationToken);
Named map projections are supported as well:
var people = await _dbContext.Persons
.ProjectTo<PersonDto>(_mapify, "Masked")
.ToArrayAsync(cancellationToken);
If you use the static Mapper API (Mapper.AddMap(...)), you can call:
var people = _dbContext.Persons
.ProjectTo<PersonDto>()
.OrderBy(x => x.Name)
.ToArray();
ProjectTo uses the same runtime fallback behavior as Map, including:
- collection-shape fallback (for example
IEnumerable<TSrc>map reused forList<TSrc>) - collection-to-single-object fallback (for example
IEnumerable<TSrc> -> Dtoreused forList<TSrc> -> Dto) - collection-to-collection materialization fallback (for example
IEnumerable<TSrc> -> IEnumerable<TDest>reused forList<TSrc> -> IReadOnlyCollection<TDest>) - nested collection fallback at multiple depths when only element maps exist (for example
List<List<List<TSrc>>> -> List<List<List<TDest>>>viaTSrc -> TDest)
For named maps, both APIs follow the same rule:
GetMap<TSource, TTarget>("Name")may returnnullwhen the named map is missingGetRequiredMap<TSource, TTarget>("Name")throws when the named map is missing
UseMap works with expressions too (not only direct properties). For example, you can filter children before mapping:
public class StudentProfile : MapifyProfile {
protected override void Configure() {
CreateMap<Student, StudentDto>();
CreateMap<Classroom, ClassroomDto>(x => new ClassroomDto {
Students = UseMap<IEnumerable<Student>, IEnumerable<StudentDto>>(
x.Students.Where(s => s.Name != null)
)
});
}
}
public async Task<IEnumerable<PersonDto>> GetPersonDtosAsync(int skip, int take, CancellationToken cancellationToken = default) {
var mapExpr = _mapify.GetMap<Person, PersonDto>();
return await _dbContext.Persons
.Select(mapExpr)
.OrderBy(x => x.Name)
.Skip(skip)
.Take(take)
.ToArrayAsync(cancellationToken);
}
The same pattern works for named maps as well:
Students = UseMap<IEnumerable<Student>, IEnumerable<StudentDto>>(
"Upper",
x.Students.Where(s => s.Name != null)
)
UseMap can also be used inside calculations. For example, map measurements first,
then take the maximum Fahrenheit value and convert it to Celsius:
public class MeasurementProfile : MapifyProfile {
protected override void Configure() {
CreateMap<Measurement, MeasurementDto>();
CreateMap<WeatherSample, WeatherSampleDto>(x => new WeatherSampleDto {
MaxTemperatureCelsius = (
UseMap<IEnumerable<Measurement>, IEnumerable<MeasurementDto>>(x.Measurements)
.OrderBy(m => m.Fahrenheit)
.Max(m => m.Fahrenheit)
- 32m) * 5m / 9m
});
}
}
Chaining also works for named maps:
Addresses = UseMap<IEnumerable<Address>, IEnumerable<AddressDto>>("Postal", x.Addresses)
.OrderBy(dto => dto.StreetName)
For EF/EF Core projections, if you apply ordering or other sequence operators after UseMap, materialize the sequence for stable provider translation:
Addresses = UseMap<IEnumerable<Address>, IEnumerable<AddressDto>>(x.Addresses)
.OrderBy(dto => dto.StreetName)
.ToList()
Inside CreateMap expressions you can also use ProjectTo<TTarget>() on enumerable members.
Mapify rewrites this marker to the same internal behavior as UseMap.
CreateMap<Person, PersonDto>(x => new PersonDto {
Addresses = x.Addresses.ProjectTo<AddressDto>().ToArray()
});
The marker also supports named mappings:
CreateMap<Person, PersonDto>("Masked", x => new PersonDto {
Addresses = x.Addresses.ProjectTo<AddressDto>("Postal").ToArray()
});
5. Runtime Parameters (instance mapper)
Runtime parameters are supported by the instance mapper (IMapify) and work in both:
- in-memory mapping (
Map(...)) - queryable projection (
ProjectTo(..., mapify, parameters))
Use Parameter<T>(string name) inside a profile map:
public class MyProfile : MapifyProfile {
protected override void Configure() {
CreateMap<Request, Result>(r => new Result {
ScoreCategory = r.Score >= Parameter<int>("minScore") ? "Pass" : "Fail"
});
}
}
Provide parameter values at execution time via IMapify:
GetMap<TSource, TTarget>(parameters)/GetRequiredMap<TSource, TTarget>(parameters)Map<TSource, TTarget>(source, parameters)Map<TSource, TTarget>(source, target, parameters)Map<TSource, TTarget>(source, name, parameters)Map<TSource, TTarget>(source, target, name, parameters)ProjectTo<TTarget>(mapify, parameters)and named overloads
Example:
var parameters = new Dictionary<string, object?> { ["minScore"] = 50 };
var expr = mapify.GetRequiredMap<Request, Result>(parameters);
var resultFromExpr = expr.Compile().Invoke(request);
var result = mapify.Map<Request, Result>(request, parameters);
var existing = new Result();
mapify.Map(request, existing, parameters);
var projected = dbContext.Requests
.ProjectTo<Result>(mapify, parameters)
.ToArray();
The static Mapper API does not provide parameterized Map / GetMap / GetRequiredMap / ProjectTo overloads.
Notes: parameter names are matched by key and values must be convertible to the T used in Parameter<T>(name).
Detailed Functionality ๐
Implicit Mappings
When CreateMap<TSource, TTarget>() is called, Mapify automatically generates bindings for properties where:
Names Match: Source and Destination property names are identical.
Types are Compatible:
- Exact match.
- Target is assignable from Source.
- Nullable Handling:
TโT?(Implicit cast)T?โT(Uses source value if not null, otherwise default(T))
If a map already exists for a same-name property type pair (e.g.
Address -> AddressDto), Mapify uses that map implicitly before falling back to direct assignment.
Mapping Type Resolution Precedence
Mapify uses the same runtime type-resolution rules for:
Map(source)/Map(source, target)- top-level
ProjectTo<TTarget>(...) - nested map resolution inside
CreateMap(UseMapandProjectTomarkers)
When Mapify resolves a map candidate, it follows this precedence:
- Exact source/target type match
- Nullable underlying-type fallback
- Assignable collection-shape fallback (source side, destination side, or both)
- Collection element-type fallback (only when both sides are collection shapes)
1) Exact map wins
If both are registered, exact type map is preferred.
CreateMap<NumberSource, NumberDto>(x => new NumberDto { Value = x.Value + 1 });
CreateMap<NumberSource?, NumberDto?>(x => x == null ? null : new NumberDto { Value = x.Value.Value + 100 });
CreateMap<Order, OrderDto>(); // Order.Number: NumberSource? -> OrderDto.Number: NumberDto?
Order.Number uses NumberSource? -> NumberDto? (exact), not the non-nullable fallback map.
2) Nullable fallback when exact nullable map is missing
If only NumberSource -> NumberDto exists, Mapify can lift/adapt it for nullable variants where compatible.
CreateMap<NumberSource, NumberDto>(x => new NumberDto { Value = x.Value + 1 });
CreateMap<Order, OrderDto>(); // NumberSource? -> NumberDto?
Mapify uses the non-nullable map and injects null-safe adaptation.
3) Collection map precedence
For collection-related resolution, Mapify resolves in this order:
- exact collection pair (
SrcShape -> DstShape) - assignable collection-shape candidates, using this hierarchy:
IList<T>ICollection<T>IReadOnlyList<T>IReadOnlyCollection<T>IEnumerable<T>
- element map fallback (
SrcElement -> DstElement) when both sides are collections
Recommendation: when you want a single collection map to work across most concrete collection shapes, register it with IEnumerable<T> (for example IEnumerable<TSrc> -> IEnumerable<TDest> or IEnumerable<TSrc> -> Dto). This map can then be reused for sources like arrays, List<T>, ICollection<T>, and IReadOnlyCollection<T> via assignable collection-shape fallback. If a specific collection shape needs different behavior, register that specific map as wellโexact matches and higher-priority shape candidates still win.
CreateMap<Item, ItemDto>(x => new ItemDto { Value = x.Value + 1 });
CreateMap<List<Item>, List<ItemDto>>(x => x.Select(i => new ItemDto { Value = i.Value + 100 }).ToList());
CreateMap<Batch, BatchDto>(); // List<Item> -> List<ItemDto>
Batch.Items uses List<Item> -> List<ItemDto> (exact collection map).
If that map is removed but an IEnumerable<Item> -> IEnumerable<ItemDto> map exists, that map is used and materialized to the destination collection type.
If neither collection map exists, Mapify falls back to Item -> ItemDto per element.
Collection-to-single-object fallback is also supported. If only
IEnumerable<Item> -> SummaryDto is registered, Mapify can resolve
List<Item> -> SummaryDto, ICollection<Item> -> SummaryDto, etc.
Likewise, if only IEnumerable<Item> -> IEnumerable<ItemDto> is registered,
Mapify can resolve mixed collection shape combinations such as
List<Item> -> IReadOnlyCollection<ItemDto>.
For nullable collection members, Mapify preserves null safely for supported query/provider shapes.
When multiple assignable collection candidates match, Mapify picks the highest-ranked candidate from the hierarchy above.
4) Collection fallback + initializer preservation
When Mapify needs a fallback value for a collection member (for example source is null, or destination member has no matching source member), it applies these rules:
- If the destination member is already initialized in the class/constructor, that user-defined value is preserved.
- Otherwise, if the destination collection is non-nullable (or marked
required), Mapify uses an empty collection/array fallback where possible. - Otherwise (nullable destination collection), Mapify uses
null.
For non-collection members, fallback remains default(T).
In short:
- source member exists โ map from source value
- source member missing โ keep user initializer when present, otherwise fallback by nullability/required rules
- source value is
null+ non-nullable collection destination โ empty collection fallback
If a custom collection type implements multiple different IEnumerable<T> element types, Mapify throws a descriptive configuration error because element type inference would be ambiguous.
GetMap / GetRequiredMap exact-type rule
GetMap<TSource, TTarget>() and GetRequiredMap<TSource, TTarget>() always resolve by the exact requested pair.
They do not return a different pair's map (for example, element maps or nullable underlying-type maps).
Examples:
- If only
Item -> ItemDtois registered,GetMap<List<Item>, List<ItemDto>>()isnull(unless default-map fallback is enabled, in which case Mapify creates a new exactList<Item> -> List<ItemDto>expression). - If only
NumberSource -> NumberDtois registered,GetMap<NumberSource?, NumberDto?>()isnull(same default fallback behavior applies).
This keeps GetMap/GetRequiredMap type-safe: the returned expression always matches the exact generic types requested.
Named Mappings
Named mappings let you register multiple mappings for the same source/target type pair.
public class PersonValueProfile : MapifyProfile {
protected override void Configure() {
CreateMap<Person, string>("FullName", x => x.FirstName + " " + x.LastName);
CreateMap<Person, string>("Initials", x => x.FirstName.Substring(0, 1) + x.LastName.Substring(0, 1));
}
}
var fullName = mapify.Map<Person, string>(person, "FullName");
var initials = mapify.Map<Person, string>(person, "Initials");
Rules:
- A default map and named maps can coexist for the same
TSource -> TTarget. - Each
(TSource, TTarget, Name)combination must be unique. UseMapcan target named maps viaUseMap<TSource, TTarget>("Name", sourceExpression).
Static API (advanced scenarios)
The static Mapper API is still fully supported, but typically used in advanced scenarios.
Static map declarations
using Mapify.NET;
using System.Linq.Expressions;
public static class PersonMappings {
public static readonly Expression<Func<Person, PersonDto>> PersonToPersonDto =
Mapper.CreateMap<Person, PersonDto>(p => new PersonDto {
Name = $"{p.FirstName} {p.LastName}",
MainAddress = AddressMappings.AddressToAddressDto.Invoke(p.MainAddress)
});
}
public static class AddressMappings {
public static readonly Expression<Func<Address, AddressDto>> AddressToAddressDto =
Mapper.CreateMap<Address, AddressDto>();
}
Static API with Entity Framework + LINQKit
If your static expressions use .Invoke(...), combine with LINQKit and .AsExpandable().
public async Task<IEnumerable<PersonDto>> GetPersonDtosAsync(int skip, int take, CancellationToken cancellationToken = default) {
return await _dbContext.Persons
.AsExpandable()
.Select(PersonMappings.PersonToPersonDto)
.OrderBy(x => x.Name)
.Skip(skip)
.Take(take)
.ToArrayAsync(cancellationToken);
}
AsExpandable() is the key piece that lets EF translate expressions that use .Invoke().
Use the package that matches your scenario:
LinqKit.Core: expression composition utilities (PredicateBuilder,Invoke,Expand) without EF integration.LinqKitorLinqKit.EntityFramework: for Entity Framework 6.x.LinqKit.Microsoft.EntityFrameworkCore: for Entity Framework Core.
For EF Core, the package ID stays the same (LinqKit.Microsoft.EntityFrameworkCore), but the major version should match EF Core major.
Global Configuration & Static Maps
You can register mappings globally to use the static Mapper.Map convenience methods.
// Register a map globally
Mapper.AddMap(PersonMappings.PersonToPersonDto);
// Or create and add in one step
Mapper.CreateAndAddMap<Person, PersonDto>(p => new PersonDto { ... });
// Use the global map anywhere
var dto = Mapper.Map<Person, PersonDto>(person);
Mapping to existing instances (instance + static)
Map(source, target) updates a provided target instance in-place only when the selected map expression is an object initializer (MemberInit).
CreateMap<Person, PersonDto>(p => new PersonDto { Name = p.FirstName });
// instance mapper
var dto1 = new PersonDto();
mapper.Map(person, dto1);
// static mapper
var dto2 = new PersonDto();
Mapper.Map(person, dto2);
Implementation note: Mapify converts initializer bindings into assignments (for example target.Name = ...) and compiles them as Action<TSource, TTarget>. If the map returns a value directly (for example CreateMap<User, string>(u => "User" + u.Id)), Map(source, target) throws NotSupportedException.
For value mappings, use Map(source) and assign the returned value.
Value Mappings (non-initializer)
Mapify also supports mappings where the expression returns a value directly (not new TTarget { ... }).
Mapper.AddMap<Person, string>(x => x.FirstName);
Mapper.AddMap<SourceStatus, TargetStatus>(x => x == SourceStatus.Active ? TargetStatus.Enabled : TargetStatus.Disabled);
var name = Mapper.Map<Person, string>(person);
var status = Mapper.Map<SourceStatus, TargetStatus>(SourceStatus.Active);
Note: value mappings are supported for
Map(source)only.Map(source, target)requires an object-initializer mapping.
Explicit Overrides & Coalescing
You can provide a partial initializer to override specific properties. Mapify also rewrites null-coalescing operators (??) to conditional expressions (x != null ? x : y) to ensure compatibility with all LINQ providers (some EF versions struggle with ??).
Mapper.CreateMap<Person, PersonDto>(p => new PersonDto {
// Explicit override
Name = p.FirstName + " " + p.LastName,
// Coalescing is rewritten for EF compatibility
Region = p.Region ?? "Unknown"
});
Caching & Performance โก
- Compiled Delegates: Accessors are compiled to delegates.
- Strategy:
- Extension Method (
.Map()): Caches the compiled delegate for that specific expression instance. - Static
Mapper.Map: Uses a global cache.- Priority: Explicitly added maps (
AddMap) take precedence over implicitly generated ones. - Auto-Cache: If global fallback is enabled via
Mapper.UseDefaultMapIfTypeMapIsMissing(true)before adding a custom map, a default map is generated and cached. CallingAddMaplater overwrites this cache entry with your custom definition.
- Priority: Explicitly added maps (
- Extension Method (
Advanced Usage ๐ ๏ธ
For advanced scenarios, Mapify exposes several lower-level methods.
Compiling Mappers Manually
If you need high-performance bulk mapping to existing objects and want to manage the delegate lifecycle yourself (referencing Action<TSource, TTarget>), you can use CompileMapper.
// Get the map expression
var mapExpr = PersonMappings.PersonToPersonDto;
// Compile to an Action<Person, PersonDto>
Action<Person, PersonDto> mapAction = Mapper.CompileMapper(mapExpr);
// Use it in a hot loop (zero dictionary lookups)
var target = new PersonDto();
foreach (var item in largeCollection) {
mapAction(item, target);
// ...
}
Retrieving Maps
You can retrieve registered maps using GetMap. This is useful in generic or dynamic contexts where you don't have direct access to the static field.
GetMap returns null if no map exists and default-map fallback is disabled.
If you want throwing behavior, use GetRequiredMap.
// Retrieve a registered map (or null)
var mapExpr = Mapper.GetMap<Person, PersonDto>();
// Enable global fallback once (if desired)
Mapper.UseDefaultMapIfTypeMapIsMissing(true);
// Then GetMap/GetRequiredMap can use generated default maps when missing
var fallbackMapExpr = Mapper.GetMap<Person, PersonDto>();
// Throw if the map is missing
var requiredMapExpr = Mapper.GetRequiredMap<Person, PersonDto>();
Strict Mode Configuration
By default, strict mode is enabled for global maps, meaning Mapper.Map<S, T>(src) throws if no map is registered. You can disable this to allow automatic fallback to default maps globally, though explicit registration is recommended for performance and control.
// Allow implicit generation of default maps if no custom map is found
Mapper.UseDefaultMapIfTypeMapIsMissing(true);
Contributing ๐ค
Contributions are welcome! Please feel free to submit a Pull Request.
License ๐
This project is licensed under the MIT License - see the LICENSE file for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. 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. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
| .NET Framework | net461 was computed. net462 is compatible. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.6.2
-
.NETStandard 2.0
-
.NETStandard 2.1
-
net10.0
-
net8.0
-
net9.0
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 |
|---|---|---|
| 3.1.8 | 94 | 5/31/2026 |
| 3.1.7 | 145 | 5/4/2026 |
| 3.1.6 | 93 | 5/3/2026 |
| 3.1.5 | 163 | 4/20/2026 |
| 3.1.4 | 95 | 4/20/2026 |
| 3.1.3 | 105 | 4/7/2026 |
| 3.1.2 | 93 | 4/7/2026 |
| 3.1.1 | 93 | 4/7/2026 |
| 3.1.0 | 94 | 4/7/2026 |
| 3.0.0 | 117 | 3/24/2026 |
| 2.3.2 | 105 | 3/18/2026 |
| 2.3.1 | 113 | 3/16/2026 |
| 2.3.0 | 263 | 3/4/2026 |
| 2.2.0 | 107 | 2/27/2026 |
| 2.1.0 | 104 | 2/27/2026 |
| 2.0.1 | 103 | 2/27/2026 |
| 2.0.0 | 106 | 2/26/2026 |
| 1.5.6 | 101 | 2/26/2026 |
| 1.5.5 | 102 | 2/25/2026 |
| 1.5.4 | 97 | 2/25/2026 |