.NET Object Mapper Guide: Transitio.Mapper with Expression Trees
If you've worked with .NET for any length of time, you've reached for an object mapper — probably AutoMapper. It works, but as projects scale, reflection-heavy mapping starts to show its cost: slower runtime performance and configuration that's hard to reason about at scale.
Transitio.Mapper is a lightweight, high-performance object mapping framework for .NET, built around expression-based mapping with support for profiles, nested mapping, and first-class dependency injection.
Installing Transitio.Mapper
dotnet add package Transitio.Mapper
# optional: Microsoft.Extensions.DependencyInjection integration
dotnet add package Transitio.Dependency
Quick Start
using Transitio.Mapper;
public class User { public string Name { get; set; } = ""; public int Age { get; set; } }
public class UserDto { public string Name { get; set; } = ""; public int Age { get; set; } }
var config = new TransitioMapperConfiguration(cfg =>
{
cfg.CreateMap<User, UserDto>();
});
IMapper mapper = config.BuildMapper();
var dto = mapper.Map<UserDto>(new User { Name = "Hitesh", Age = 30 });
// dto.Name == "Hitesh", dto.Age == 30
Notice the single generic argument on Map<UserDto>(user) — Transitio infers the source type, so you only specify the destination.
Core Features
Custom member mapping
cfg.CreateMap<User, UserViewDto>()
.ForMember(dest => dest.DisplayName, opt => opt.MapFrom(src => src.Name.ToUpper()));
Ignoring a property
cfg.CreateMap<User, UserDto>()
.ForMember(dest => dest.Age, opt => opt.Ignore()); // Age keeps its default
Conditional mapping
cfg.CreateMap<User, UserViewDto>()
.ForMember(dest => dest.Age, opt => opt.Condition(src => src.Age >= 18));
Reverse mapping
cfg.CreateMap<User, UserDto>().ReverseMap();
var dto = mapper.Map<UserDto>(user); // User -> UserDto
var back = mapper.Map<User>(dto); // UserDto -> User
Nested and collection mapping — resolved automatically once both types are registered:
cfg.CreateMap<User, UserDto>();
cfg.CreateMap<Order, OrderDto>(); // Order.Customer (User) mapped via the User -> UserDto map
cfg.CreateMap<Cart, CartDto>(); // Cart.Orders (List<Order>) mapped element-by-element
var cartDto = mapper.Map<CartDto>(cart);
var array = mapper.Map<OrderDto[]>(orders); // IEnumerable -> array
var asList = mapper.Map<IList<OrderDto>>(orders); // IEnumerable -> interface
Configuration validation — catch mapping mistakes early instead of at runtime:
config.AssertConfigurationIsValid(); // throws if a destination property has no valid source
Organizing Maps with Profiles
For anything beyond a handful of maps, group them into a MappingProfile:
public class UserProfile : MappingProfile
{
public override void Configure(TransitioConfigBuilder cfg)
{
cfg.CreateMap<User, UserDto>().ReverseMap();
}
}
var config = new TransitioMapperConfiguration(cfg =>
{
cfg.AddProfile<UserProfile>();
});
Registering with Dependency Injection
With Transitio.Dependency installed, registration is a single call:
services.AddTransitio(cfg =>
{
cfg.CreateMap<User, UserDto>();
cfg.CreateMap<Order, OrderDto>();
});
Then inject IMapper directly wherever you need it:
public class UserService
{
private readonly IMapper _mapper;
public UserService(IMapper mapper) => _mapper = mapper;
public UserDto ToDto(User user) => _mapper.Map<UserDto>(user);
}
AddTransitio registers the mapper as a singleton by default, so the compiled-mapping cache is shared across the whole app. If a ConvertUsing<TConverter>() converter depends on a scoped service like a DbContext, you can register with a scoped or transient lifetime instead — see the Dependency Injection guide for the full pattern, including assembly scanning for profiles and keyed mappers.
When You'd Reach for This
- API-heavy services mapping entities to DTOs on every request
- .NET-to-cloud migration projects where you're reshaping legacy models into new service contracts — a scenario I run into constantly in AWS migration work
- Any codebase where you want mapping mistakes caught by
AssertConfigurationIsValid()at startup, rather than discovered in production
What's Next
Transitio.Mapper is one piece of a small, focused open-source ecosystem I'm building under the Transitio name, alongside Transitio.Dependency and the upcoming Transitio.Validator.
- NuGet: Transitio.Mapper · Transitio.Dependency
- GitHub: github.com/Transitio/Transitio
- Docs: Getting Started · Core Mapping Features · Dependency Injection
The next post in this series covers pairing Transitio.Mapper with Transitio.Dependency in a real migration pipeline.
Have questions or found an edge case? Open an issue on GitHub or reach out on X @hstarkar87.

Comments
Post a Comment