eQuantic Mapper
Object mapping utilities and extensions
Version: 2.0.0
Downloads: 15K+
Status: stable
Category: utilities
Installation
dotnet add package eQuantic.Mapper

Overview
eQuantic.Mapper is a high-performance, compile-time object mapping library for .NET that eliminates reflection overhead by generating mapping code at build time using source generators. It provides powerful features for complex property mappings, aggregations, conditional mapping, and bidirectional transformations with excellent performance characteristics.
Unlike traditional mappers that rely on runtime reflection, eQuantic.Mapper uses Roslyn analyzers to generate optimized mapping code during compilation, resulting in zero reflection overhead and superior performance. The library supports advanced scenarios including property aggregation, conditional mapping based on runtime conditions, context-aware mappings, and bidirectional mappings.
The library is designed for enterprise applications where performance, type safety, and maintainability are critical. It integrates seamlessly with dependency injection containers and provides rich customization options through attributes and partial classes, enabling developers to create sophisticated mapping scenarios while maintaining clean, readable code.
Quick Start
1. Installation and Setup
Install both the core mapper package and the source generator to enable compile-time code generation.
// Install via Package Manager Console
Install-Package eQuantic.Mapper
Install-Package eQuantic.Mapper.Generator
// Or via .NET CLI
dotnet add package eQuantic.Mapper
dotnet add package eQuantic.Mapper.Generator
// Basic using statements
using eQuantic.Mapper;
using eQuantic.Mapper.Attributes;
2. Define Your Models
Create your source and destination models. Use attributes to configure advanced mapping scenarios like property aggregation and conditional mapping.
// Source model
public class UserSource
{
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public int Age { get; set; };
public decimal Salary { get; set; };
public decimal Bonus { get; set; };
public bool IsActive { get; set; };
}
// Destination model with aggregation
public class UserDestination
{
// Property aggregation
[MapFrom(typeof(UserSource),
new[] { nameof(UserSource.FirstName), nameof(UserSource.LastName) },
MapperPropertyAggregation.ConcatenateWithSpace)]
public string FullName { get; set; } = string.Empty;
// Numeric aggregation
[MapFrom(typeof(UserSource),
new[] { nameof(UserSource.Salary), nameof(UserSource.Bonus) },
MapperPropertyAggregation.Sum)]
public decimal TotalIncome { get; set; };
// Conditional mapping
[MapFrom(typeof(UserSource), nameof(UserSource.Age))]
[MapWhen(nameof(UserSource.IsActive))]
public int? DisplayAge { get; set; };
// Auto-mapped properties (by name)
public int Age { get; set; }
}
3. Create Your Mapper
Define a partial class with the Mapper attribute. The source generator will create the implementation automatically.
// Auto-generated mapper
[Mapper(typeof(UserSource), typeof(UserDestination))]
public partial class UserMapper : IMapper
{
// Optional: Add custom logic
partial void AfterConstructor()
{
OnBeforeMap += (sender, args) =>
{
// Pre-processing logic
Console.WriteLine($"Mapping user: {args.Source.FirstName}");
};
OnAfterMap += (sender, args) =>
{
// Post-processing logic
if (args.Destination.Age < 18)
{
args.Destination.FullName = $"Minor: {args.Destination.FullName}";
}
};
}
}
4. Configure Dependency Injection
Register all mappers in your DI container using the provided extension method.
var builder = WebApplication.CreateBuilder(args);
// Register all mappers automatically
builder.Services.AddMappers();
var app = builder.Build();
// Use in controllers or services
app.MapGet("/users/{id}", async (int id, IMapperFactory mapperFactory) =>
{
var mapper = mapperFactory.GetMapper<UserSource, UserDestination>()!;
var user = await GetUserAsync(id); // Your data access logic
return mapper.Map(user);
});
app.Run();
5. Advanced Features Usage
Leverage advanced features like bidirectional mapping, context-aware mapping, and conditional logic.
// Bidirectional mapping
[Mapper(typeof(UserSource), typeof(UserDestination), MapperDirection.Bidirectional)]
public partial class BidirectionalUserMapper : IMapper { }
// Context-aware mapping
public class MappingContext
{
public bool IncludeSensitiveData { get; set; }
public string UserRole { get; set; } = "User";
}
[Mapper(typeof(UserSource), typeof(UserDestination), typeof(MappingContext))]
public partial class ContextUserMapper : IMapper<UserSource, UserDestination, MappingContext>
{
partial void AfterConstructor()
{
OnAfterMap += (sender, args) =>
{
if (!Context?.IncludeSensitiveData == true)
{
args.Destination.Salary = 0;
}
};
}
}
// Usage
var contextMapper = mapperFactory.GetMapper<UserSource, UserDestination, MappingContext>()!;
contextMapper.Context = new MappingContext { IncludeSensitiveData = false, UserRole = "User" };
var result = contextMapper.Map(userSource);
API Reference
MapperAttribute
Defines a mapper class for transforming between two types, with optional context and direction support.
[Mapper(typeof(UserSource), typeof(UserDestination))]
public partial class UserMapper : IMapper
{
// Auto-generated implementation
}
// With context
[Mapper(typeof(UserSource), typeof(UserDestination), typeof(MappingContext))]
public partial class ContextUserMapper : IMapper<UserSource, UserDestination, MappingContext> { }
// Bidirectional
[Mapper(typeof(UserSource), typeof(UserDestination), MapperDirection.Bidirectional)]
public partial class BiMapper : IMapper { }
MapFromAttribute
Maps a destination property from one or more source properties with optional aggregation.
// Simple mapping
[MapFrom(typeof(UserSource), nameof(UserSource.FirstName))]
public string Name { get; set; }
// Property aggregation
[MapFrom(typeof(UserSource),
new[] { nameof(UserSource.FirstName), nameof(UserSource.LastName) },
MapperPropertyAggregation.ConcatenateWithSpace)]
public string FullName { get; set; }
// Numeric aggregation
[MapFrom(typeof(UserSource),
new[] { nameof(UserSource.Salary), nameof(UserSource.Bonus) },
MapperPropertyAggregation.Sum)]
public decimal TotalIncome { get; set; }
MapWhenAttribute
Conditionally maps a property based on a boolean property or C# expression.
// Simple boolean condition
[MapFrom(typeof(UserSource), nameof(UserSource.Email))]
[MapWhen(nameof(UserSource.IsEmailVisible))]
public string? PublicEmail { get; set; }
// Expression condition
[MapFrom(typeof(UserSource), nameof(UserSource.Age))]
[MapWhen("source.Age >= 18", true)]
public int? DisplayAge { get; set; }
// Context-aware condition
[MapFrom(typeof(UserSource), nameof(UserSource.Salary))]
[MapWhen("Context?.IncludeSensitiveData == true", true)]
public decimal? Salary { get; set; }
IMapper<TSource, TDestination>
Basic mapper interface for transforming from source to destination type.
public class UserMapper : IMapper<UserSource, UserDestination>
{
public UserDestination? Map(UserSource? source)
{
if (source == null) return null;
return new UserDestination
{
FirstName = source.FirstName,
LastName = source.LastName
};
}
public UserDestination? Map(UserSource? source, UserDestination? destination)
{
// Map to existing instance
}
}
IMapper<TSource, TDestination, TContext>
Context-aware mapper interface with support for mapping context.
public class ContextUserMapper : IMapper<UserSource, UserDestination, MappingContext>
{
public MappingContext? Context { get; set; }
public UserDestination? Map(UserSource? source)
{
if (source == null) return null;
var result = new UserDestination();
if (Context?.IncludeSensitiveData == true)
{
result.Salary = source.Salary;
}
return result;
}
}
IMapperFactory
Factory interface for creating and retrieving mapper instances.
// Dependency injection usage
public class UserController : ControllerBase
{
private readonly IMapperFactory _mapperFactory;
public UserController(IMapperFactory mapperFactory)
{
_mapperFactory = mapperFactory;
}
[HttpGet("{id}")]
public async Task<UserDto> GetUser(int id)
{
var mapper = _mapperFactory.GetMapper<User, UserDto>()!;
var user = await _userService.GetByIdAsync(id);
return mapper.Map(user);
}
}
MapperDirection
Specifies the direction of mapping for bidirectional mappers.
// Forward only (default)
[Mapper(typeof(UserSource), typeof(UserDestination))]
public partial class ForwardMapper : IMapper { }
// Reverse only
[Mapper(typeof(UserSource), typeof(UserDestination), MapperDirection.Reverse)]
public partial class ReverseMapper : IMapper { }
// Bidirectional
[Mapper(typeof(UserSource), typeof(UserDestination), MapperDirection.Bidirectional)]
public partial class BiMapper : IMapper { }
MapperPropertyAggregation
Defines how multiple source properties are combined into a single destination property.
// String concatenation
[MapFrom(typeof(PersonSource),
new[] { nameof(PersonSource.FirstName), nameof(PersonSource.LastName) },
MapperPropertyAggregation.ConcatenateWithSpace)]
public string FullName { get; set; }
// Numeric operations
[MapFrom(typeof(PersonSource),
new[] { nameof(PersonSource.Salary), nameof(PersonSource.Bonus) },
MapperPropertyAggregation.Sum)]
public decimal TotalIncome { get; set; }
[MapFrom(typeof(PersonSource),
new[] { nameof(PersonSource.Score1), nameof(PersonSource.Score2) },
MapperPropertyAggregation.Average)]
public decimal AverageScore { get; set; }
Key Features
Zero Reflection with Source Generation
Uses Roslyn analyzers to generate highly optimized mapping code at compile time, completely eliminating reflection overh...
Property Aggregation System
Combines multiple source properties into single destination properties using various aggregation types including concate...
Conditional Mapping with MapWhen
Maps properties only when specific conditions are met using the MapWhenAttribute. Supports simple boolean property condi...
Bidirectional Mapping Support
Generate mappers that work in both directions (Forward, Reverse, or Bidirectional) with a single class definition using ...
Context-Aware Mapping
Support for context objects that influence mapping behavior, enabling scenarios like user role-based field mapping, tena...
Dependency Injection Integration
Built-in support for dependency injection with IMapperFactory for creating and managing mapper instances, automatic regi...