ObjectMap.NET
1.0.6
See the version list below for details.
dotnet add package ObjectMap.NET --version 1.0.6
NuGet\Install-Package ObjectMap.NET -Version 1.0.6
<PackageReference Include="ObjectMap.NET" Version="1.0.6" />
<PackageVersion Include="ObjectMap.NET" Version="1.0.6" />
<PackageReference Include="ObjectMap.NET" />
paket add ObjectMap.NET --version 1.0.6
#r "nuget: ObjectMap.NET, 1.0.6"
#:package ObjectMap.NET@1.0.6
#addin nuget:?package=ObjectMap.NET&version=1.0.6
#tool nuget:?package=ObjectMap.NET&version=1.0.6
ObjectMap.NET
A lightweight, convention-based object mapper for modern .NET.
NuGet: ObjectMap.NET
Namespaces: SimpleMapper.*
Target framework: net10.0
ObjectMap.NET is designed for explicit maps — every (source → destination) pair is registered in a profile. Convention handles the common cases; fluent configuration covers the rest.
Installation
dotnet add package ObjectMap.NET
Local project reference:
<ProjectReference Include="path\to\SR.ObjectMapper.NET.csproj" />
Quick start
1. Register the mapper
using SimpleMapper.Mapper.DependencyInjection;
using SimpleMapper.Mapper.Profiles;
services.AddSRSimpleMapper(cfg =>
{
cfg.RegisterProfile(new MyAppMappingProfile());
});
Optional global settings:
services.AddSRSimpleMapper(cfg =>
{
cfg.DestinationMemberNaming = NamingConventions.SnakeCaseDestination;
cfg.EnableAutoFlattening = true;
cfg.RegisterProfile<MyAppMappingProfile>();
});
2. Define a profile
using SimpleMapper.Mapper.Profiles;
public class MyAppMappingProfile : Profile
{
protected override void ConfigureMaps()
{
CreateMap<OrderDto, OrderEntity>()
.ForMember(d => d.Total, o => o.MapFrom(s => s.Lines.Sum(l => l.Amount))
.BeforeMap((dto, entity) => { /* runs before members are assigned */ })
.AfterMap((dto, entity) => { /* runs after mapping completes */ })
.ReverseMap();
CreateMap<LineItemDto, LineItemEntity>();
}
}
3. Inject and map
public class OrderService(ISRSimpleMapper mapper)
{
public OrderEntity ToEntity(OrderDto dto) =>
mapper.Map<OrderDto, OrderEntity>(dto);
}
Core API (ISRSimpleMapper)
| Method | Purpose |
|---|---|
Map<TDestination>(object source) |
Map using the runtime type of source. |
Map<TSource, TDestination>(TSource source) |
Map with compile-time source and destination types (preferred). |
Map<TSource, TDestination>(TSource source, TDestination destination) |
Map into an existing instance (update / patch). |
ProjectTo<TSource, TDestination>(IQueryable<TSource> query) |
Project a query using a cached expression tree (EF Core–friendly). |
Nulls: Map<TSource, TDestination>(source) returns default when source is null.
Map(source, destination) throws ArgumentNullException if either argument is null.
Configuration
MapperConfiguration
| Property | Description |
|---|---|
DestinationMemberNaming |
How destination member names relate to source names (default: PascalCase). |
SourceMemberNaming |
Additional source-side naming convention for property lookup. |
EnableAutoFlattening |
When true, nested source properties can populate flat destination members (default: true). |
Built-in naming conventions (NamingConventions):
PascalCase— default, same names on both sides.SnakeCaseDestination— source PascalCase → destination snake_case (e.g.UserName→user_name).SnakeCaseSource— source snake_case → destination PascalCase.
Profile
Override ConfigureMaps() and call CreateMap<TSource, TDestination>() for every pair you map.
Fluent mapping API
CreateMap<TSource, TDestination>()
Registers a one-way map. Matching is by convention (name + compatible types), plus flattening and naming rules below.
ReverseMap()
Registers the inverse map TDestination → TSource.
- Ignored members are mirrored on the reverse map.
- Flattened members are reversed into nested paths (e.g.
City→Address.City). BeforeMap/AfterMaphooks are copied with swapped(source, destination)arguments so forward hooks run on the correct instances during reverse mapping.
ForMember(dest => dest.Member, opt => { ... })
| Call | Effect |
|---|---|
opt.Ignore() |
Skip this destination member. |
opt.MapFrom(src => ...) |
Set the member from a custom expression. |
BeforeMap / AfterMap
CreateMap<Entity, Dto>()
.BeforeMap((src, dest) => { /* destination exists; members not yet assigned */ })
.AfterMap((src, dest) => { /* all mapping complete */ });
On create-new maps, BeforeMap runs after an empty destination is created and before any members are assigned.
DisableAutoFlattening()
Disables automatic flattening for a specific map (e.g. when Address.City must not map to City).
Auto-flattening
When enabled, nested scalar properties can populate flat destination members:
// Entity.Address.City → FlatDto.City
CreateMap<Entity, FlatDto>();
- Supports multi-level paths (e.g.
A.B.C.Name→Name). - Ambiguous paths (multiple nested matches) throw
MappingException— use explicitForMemberor disable flattening. - Works in runtime mapping, compiled maps, reverse maps, and
ProjectTo.
How mapping works
- Create destination (parameterless constructor, or uninitialized object when none exists).
BeforeMaphooks run.- Compiled assignment (
CompileToExisting) sets scalar and simple custom members. - Post-compile runtime handles flatten-reverse members, complex custom
MapFrom, nested objects, and collections. AfterMaphooks run.
Collections and nested objects are mapped at runtime using registered element/nested type maps. Flat scalars use compiled expressions for speed.
Collections
- Different element types are mapped element-wise (never copied by reference).
- Register
CreateMap<TSourceElement, TDestinationElement>for each element pair. - Strings are not treated as collections.
CreateMap<OrderDto, OrderEntity>();
CreateMap<LineItemDto, LineItemEntity>();
// OrderDto.Lines → OrderEntity.Lines
Supported targets include List<T>, arrays, ICollection<T>, HashSet<T>, Queue<T>, and Stack<T>.
Mapping into an existing object
mapper.Map(orderDto, trackedEntity);
Use for EF updates: load the entity, map the DTO onto it.
- Flattened reverse members (e.g.
City→Address.City) are supported. - Collection properties are updated via nested mapping (not in the initial convention loop).
- Ignored and explicit
ForMembermembers are respected.
ProjectTo (database projection)
var dtos = dbContext.Orders
.AsNoTracking()
.ProjectTo<OrderEntity, OrderDto>(mapper)
.ToList();
- Builds a cached LINQ expression tree per type map (not per-row
Mapcalls). - Works with EF Core for convention-based and flattening maps.
- Simple computed
MapFromexpressions (e.g.s => s.X * 2) can be included when translatable. - Throws
MappingExceptionat build time when a configured member cannot be translated. - Arbitrary method calls in
MapFrommay build an expression that EF cannot translate to SQL — test your queries.
Prefer ProjectTo for list/query endpoints instead of loading entities and calling Map in memory.
Dependency injection
No changes required beyond registration:
services.AddSRSimpleMapper(cfg => cfg.RegisterProfile(new MyAppMappingProfile>());
ISRSimpleMapper is registered as singleton; MapperConfiguration and compiled maps are built once.
Entity Framework notes
- Updates:
Map(dto, trackedEntity)— do not create a new entity and attach. - Queries: Use
ProjectTowithAsNoTracking()for read-only lists. - Owned types / nested value objects: Ensure your EF model matches flatten paths (e.g.
OwnsOne(e => e.Address)). - Proxies: Nested mapping normalizes
Castle.Proxiesto base types when resolving nested maps.
Performance guidance
| Scenario | Recommendation |
|---|---|
| API list endpoints | Paginate at the DB; use ProjectTo. |
| Flat DTOs | Fast — compiled scalar mapping. |
| Nested graphs in memory | Slower — reflection per object; fine for small/medium batches. |
| 100k+ nested records | Avoid mapping entire graphs in one shot; process in chunks. |
Requirements checklist
- Register
CreateMap<Source, Dest>for every pair you callMapwith. - Register element maps for collection properties with different element types.
- Use
Ignore()for properties maintained elsewhere (passwords, manual navigations). - Use
ForMember/MapFromfor computed or renamed members. - Use explicit
ForMemberorDisableAutoFlattening()when flatten paths are ambiguous.
Troubleshooting
| Symptom | Likely cause |
|---|---|
No map found from X to Y |
Missing CreateMap<X, Y> for the exact CLR types. |
Ambiguous flattening for member 'X' |
Multiple nested paths match; add explicit ForMember or disable flattening. |
Cannot translate member 'X' to a database projection |
MapFrom cannot be expressed for ProjectTo; simplify or map in memory. |
MappingException from MapFrom |
Null reference or invalid expression in custom mapping. |
| Collection empty | Missing element CreateMap, or property ignored / skipped. |
| Reverse map missing nested value | Ensure ReverseMap() is called; flattened reverse requires auto-flatten on forward map. |
EF query fails after ProjectTo |
Expression uses non-translatable method calls; inspect SQL or simplify MapFrom. |
License
MIT
Author
Shohanur Rahman
LinkedIn
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
-
net10.0
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
See PackageReleaseNotes.md in the package root. Highlights (1.0.4): enum ↔ underlying integral mapping (compiled + runtime); int ↔ string; string → int; decimal → double; bool → string; string ↔ Guid documented with other scalars. Prior: map-into-existing collection fixes; nested Ignore/ForMember; ReverseMap Ignore; MapFrom collection fallback; int→int? compile fix; string↔Guid; netstandard2.0 ToHashSet.