A lightweight, convention-based object-to-object mapping library for .NET. Similar to AutoMapper with fluent API, profile support, and dependency injection integration.
$ dotnet add package TypeSyncA lightweight, convention-based object-to-object mapping library for .NET 8. Similar to AutoMapper with a fluent API, profile support, and dependency injection integration.
CreateMap, ForMember, ReverseMapCustomer.Name → CustomerName)IValueResolver for complex transformationsIServiceCollectiondotnet add package TypeSync
using TypeSync;
// Define your models
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
public class UserDto
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName { get; set; }
}
// Configure mappings
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, UserDto>()
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => $"{src.FirstName} {src.LastName}"));
});
// Create mapper and map objects
IMapper mapper = config.CreateMapper();
var user = new User { Id = 1, FirstName = "John", LastName = "Doe", Email = "john@example.com" };
var userDto = mapper.Map<User, UserDto>(user);
// userDto.FullName == "John Doe"
// Create a profile
public class UserProfile : MappingProfile
{
public UserProfile()
{
CreateMap<User, UserDto>()
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => $"{src.FirstName} {src.LastName}"));
CreateMap<Order, OrderDto>()
.ReverseMap();
}
}
// Register profiles
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<UserProfile>();
});
using TypeSync.DependencyInjection;
// In Program.cs or Startup.cs
builder.Services.AddTypeSync(cfg =>
{
cfg.CreateMap<User, UserDto>();
cfg.AddProfile<UserProfile>();
});
// Or auto-discover profiles from an assembly
builder.Services.AddTypeSync(typeof(Program).Assembly);
// Inject IMapper in your services
public class UserService
{
private readonly IMapper _mapper;
public UserService(IMapper mapper)
{
_mapper = mapper;
}
public UserDto GetUser(User user) => _mapper.Map<User, UserDto>(user);
}
public class Order
{
public Customer Customer { get; set; }
public decimal Total { get; set; }
}
public class Customer
{
public string Name { get; set; }
public string Email { get; set; }
}
public class OrderDto
{
public string CustomerName { get; set; } // Automatically mapped from Order.Customer.Name
public string CustomerEmail { get; set; } // Automatically mapped from Order.Customer.Email
public decimal Total { get; set; }
}
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Order, OrderDto>(); // Flattening is automatic!
});
TypeSync automatically maps collection properties when the element types have a defined mapping:
public class Provider
{
public string Name { get; set; }
public ICollection<Address> Areas { get; set; }
}
public class ProviderDto
{
public string Name { get; set; }
public ICollection<AddressDto> Areas { get; set; } // Collection is mapped automatically!
}
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Provider, ProviderDto>();
cfg.CreateMap<Address, AddressDto>().ReverseMap();
});
var provider = new Provider
{
Name = "Test",
Areas = new List<Address> { new Address { City = "Delhi" } }
};
var dto = mapper.Map<Provider, ProviderDto>(provider);
// dto.Areas contains the mapped AddressDto items
Supported Collection Types:
IEnumerable<T>, ICollection<T>, IList<T>List<T>, HashSet<T>T[] (arrays)Use ProjectTo<T> for efficient database queries with Entity Framework:
using TypeSync.QueryableExtensions;
// In your repository or service
var items = await dbContext.Providers
.Where(p => p.IsActive)
.ProjectTo<ProviderDto>(_mapper.ConfigurationProvider)
.ToListAsync();
This generates optimized SQL that only selects the required columns, including nested collections.
Use MapOptions to ignore properties at runtime for both Map and ProjectTo:
// For Map
var options = new MapOptions("Password", "SecretKey");
var dto = mapper.Map<User, UserDto>(user, options);
// For ProjectTo
var results = query.ProjectTo<UserDto>(config, new MapOptions("Password"));
// Or via IMapper
var results = mapper.ProjectTo<UserDto>(query, new MapOptions("InternalField"));
// Fluent syntax
var options = new MapOptions().Ignore("Email").Ignore("Phone");
var dto = mapper.Map<User, UserDto>(user, options);
cfg.CreateMap<User, UserDto>()
// Custom mapping
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => $"{src.FirstName} {src.LastName}"))
// Ignore a property
.ForMember(dest => dest.Password, opt => opt.Ignore())
// Conditional mapping
.ForMember(dest => dest.Email, opt => opt.Condition(src => src.IsEmailVisible))
// Null substitution
.ForMember(dest => dest.Nickname, opt => opt.NullSubstitute("N/A"))
// Before/After map actions
.BeforeMap((src, dest) => Console.WriteLine("Mapping started"))
.AfterMap((src, dest) => dest.MappedAt = DateTime.UtcNow)
// Custom constructor
.ConstructUsing(src => new UserDto(src.Id))
// Create reverse mapping
.ReverseMap();
public class FullNameResolver : IValueResolver<User, UserDto, string>
{
public string Resolve(User source, UserDto destination, string destMember)
{
return $"{source.FirstName} {source.LastName}";
}
}
cfg.CreateMap<User, UserDto>()
.ForMember(dest => dest.FullName, opt => opt.MapFrom<FullNameResolver>());
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, UserDto>();
});
// Throws if any destination members are unmapped
config.AssertConfigurationIsValid();
TDestination Map<TSource, TDestination>(TSource source) - Map to new objectTDestination Map<TSource, TDestination>(TSource source, MapOptions options) - Map with runtime optionsTDestination Map<TDestination>(object source) - Map using runtime typevoid Map<TSource, TDestination>(TSource source, TDestination destination) - Map to existing objectIQueryable<TDestination> ProjectTo<TDestination>(IQueryable source) - Project queryable to destination typeIQueryable<TDestination> ProjectTo<TDestination>(IQueryable source, MapOptions options) - Project with runtime optionsCreateMap<TSource, TDestination>() - Create a type mappingAddProfile<TProfile>() - Add a mapping profileAddProfilesFromAssembly(Assembly) - Auto-discover profilesCreateMapper() - Create an IMapper instanceAssertConfigurationIsValid() - Validate mappingsForMember() - Configure individual memberReverseMap() - Create reverse mappingBeforeMap() / AfterMap() - Add actionsCondition() - Add mapping conditionConstructUsing() - Custom constructionTypeSync is designed with security in mind:
Map() and ProjectTo() detect circular references in entity graphs and type hierarchies, preventing StackOverflowException with EF Core navigation properties.System.Diagnostics.Debug for troubleshooting without exposing sensitive information.MIT License