Moedim.Mapper 1.0.1

dotnet add package Moedim.Mapper --version 1.0.1
                    
NuGet\Install-Package Moedim.Mapper -Version 1.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Moedim.Mapper" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Moedim.Mapper" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Moedim.Mapper" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Moedim.Mapper --version 1.0.1
                    
#r "nuget: Moedim.Mapper, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Moedim.Mapper@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Moedim.Mapper&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Moedim.Mapper&version=1.0.1
                    
Install as a Cake Tool

Moedim.Mapper

C# Source Generator Object Mapper - Convention-based and Attribute-based mapping with compile-time code generation

Build Status NuGet License

Stand With Israel

This is a Hebrew word that translates "feast" or "appointed time." "Appointed times" refers to HaSham's festivals in Vayikra/Leviticus 23rd. The feasts are "signals and signs" to help us know what is on the heart of HaShem.

Features

  • Compile-Time Code Generation - Zero runtime overhead with source generators
  • Attribute-Based Mapping - Simple, declarative mapping configuration
  • Type-Safe - Full compile-time type checking and IntelliSense support
  • Null-Safe - Handles nullable reference types correctly
  • Nested Object Mapping - Automatically maps nested complex objects
  • Collection Support - Maps List, Array, IEnumerable with complex object transformation
  • Custom Property Mapping - Map properties with different names
  • Value Converters - Custom transformation logic for property values
  • Ignored Properties - Exclude specific properties from mapping
  • High Performance - Faster than reflection-based mappers
  • Multi-Framework Support - Targets .NET 6.0, .NET 7.0, and .NET 8.0

Hire me

Please send email if you consider to hire me.

buymeacoffee

Give a Star! ⭐

If you like or are using this project to learn or start your solution, please give it a star. Thanks!

Installation

dotnet add package Moedim.Mapper

Quick Start

Basic Attribute-Based Mapping

using Moedim.Mapper;

// Source class
public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Email { get; set; }
}

// Destination class with MapFrom attribute
[MapFrom(typeof(User))]
public class UserDto
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Email { get; set; }
}

// Usage
var user = new User { Name = "John", Age = 30, Email = "john@example.com" };
var dto = user.ToUserDto(); // Extension method generated automatically

Custom Property Mapping

public class Product
{
    public string ProductName { get; set; }
    public decimal Price { get; set; }
}

[MapFrom(typeof(Product))]
public class ProductDto
{
    [MapProperty("ProductName")]  // Map from different property name
    public string Name { get; set; }
    public decimal Price { get; set; }
}

// Usage
var product = new Product { ProductName = "Laptop", Price = 1299.99m };
var dto = product.ToProductDto();
// dto.Name will contain "Laptop"

Ignoring Properties

public class Employee
{
    public string Name { get; set; }
    public decimal Salary { get; set; }
    public string SocialSecurityNumber { get; set; }
}

[MapFrom(typeof(Employee))]
public class EmployeeDto
{
    public string Name { get; set; }

    [IgnoreProperty]  // Excluded from mapping
    public decimal Salary { get; set; }

    [IgnoreProperty]  // Excluded from mapping
    public string SocialSecurityNumber { get; set; }
}

Collection Mapping

public class Team
{
    public string Name { get; set; }
    public List<string> Members { get; set; }
}

[MapFrom(typeof(Team))]
public class TeamDto
{
    public string Name { get; set; }
    public List<string> Members { get; set; }
}

// Collections are mapped automatically
var team = new Team
{
    Name = "Dev Team",
    Members = new List<string> { "Alice", "Bob" }
};
var dto = team.ToTeamDto();

Bidirectional Mapping

// Map in both directions
[MapTo(typeof(PersonDto))]
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

[MapFrom(typeof(Person))]
public class PersonDto
{
    public string Name { get; set; }
    public int Age { get; set; }
}

// Now both directions work
var person = new Person { Name = "Alice", Age = 25 };
var dto = person.ToPersonDto();
var personAgain = dto.ToPerson();

Complex Object Mapping

Nested Object Mapping

Moedim.Mapper automatically handles nested complex objects:

public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public string ZipCode { get; set; }
}

[MapFrom(typeof(Address))]
public class AddressDto
{
    public string Street { get; set; }
    public string City { get; set; }
    public string ZipCode { get; set; }
}

public class Customer
{
    public string Name { get; set; }
    public Address DeliveryAddress { get; set; }
}

[MapFrom(typeof(Customer))]
public class CustomerDto
{
    public string Name { get; set; }
    public AddressDto DeliveryAddress { get; set; }
}

// Nested objects are automatically mapped
var customer = new Customer
{
    Name = "John Doe",
    DeliveryAddress = new Address
    {
        Street = "123 Main St",
        City = "New York",
        ZipCode = "10001"
    }
};

var dto = customer.ToCustomerDto();
// dto.DeliveryAddress will be an AddressDto with all properties mapped

Collections of Complex Objects

Map collections containing complex objects:

public class OrderItem
{
    public string ProductName { get; set; }
    public int Quantity { get; set; }
    public decimal Price { get; set; }
}

[MapFrom(typeof(OrderItem))]
public class OrderItemDto
{
    public string ProductName { get; set; }
    public int Quantity { get; set; }
    public decimal Price { get; set; }
}

public class Order
{
    public string OrderNumber { get; set; }
    public List<OrderItem> Items { get; set; }
}

[MapFrom(typeof(Order))]
public class OrderDto
{
    public string OrderNumber { get; set; }
    public List<OrderItemDto> Items { get; set; }
}

// Collection items are automatically transformed
var order = new Order
{
    OrderNumber = "ORD-001",
    Items = new List<OrderItem>
    {
        new OrderItem { ProductName = "Laptop", Quantity = 1, Price = 1299.99m },
        new OrderItem { ProductName = "Mouse", Quantity = 2, Price = 29.99m }
    }
};

var dto = order.ToOrderDto();
// dto.Items will be a List<OrderItemDto> with all items mapped

Value Converters

Apply custom transformation logic to property values:

public class TemperatureReading
{
    public double Celsius { get; set; }
    public DateTime Timestamp { get; set; }
}

// Custom converter implementation
public class CelsiusToFahrenheitConverter : IValueConverter<double, double>
{
    public double Convert(double celsius)
    {
        return (celsius * 9.0 / 5.0) + 32.0;
    }
}

[MapFrom(typeof(TemperatureReading))]
public class TemperatureReadingDto
{
    [ConvertWith(typeof(CelsiusToFahrenheitConverter))]
    public double Celsius { get; set; }  // Will contain Fahrenheit value

    public DateTime Timestamp { get; set; }
}

// Usage
var reading = new TemperatureReading { Celsius = 25.0, Timestamp = DateTime.Now };
var dto = reading.ToTemperatureReadingDto();
// dto.Celsius will be 77.0 (Fahrenheit)

Deep Nesting

Handle complex, deeply nested object graphs:

public class Company
{
    public string Name { get; set; }
    public Department MainDepartment { get; set; }
    public List<Department> AllDepartments { get; set; }
}

public class Department
{
    public string Name { get; set; }
    public Manager Manager { get; set; }
    public List<Employee> Employees { get; set; }
}

public class Manager
{
    public string Name { get; set; }
    public int YearsExperience { get; set; }
}

public class Employee
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

// Corresponding DTOs with [MapFrom] attributes...

// All levels are automatically mapped
var company = new Company
{
    Name = "TechCorp",
    MainDepartment = new Department
    {
        Name = "Engineering",
        Manager = new Manager { Name = "Alice", YearsExperience = 10 },
        Employees = new List<Employee>
        {
            new Employee { FirstName = "Bob", LastName = "Smith" },
            new Employee { FirstName = "Carol", LastName = "Jones" }
        }
    }
};

var dto = company.ToCompanyDto();
// Entire object graph is mapped recursively

Generated Code Example

For the basic User/UserDto example, Moedim.Mapper generates:

// <auto-generated/>
#nullable enable

using TestNamespace;

namespace TestNamespace;

/// <summary>
/// Extension methods for mapping User to UserDto.
/// </summary>
public static class UserToUserDtoMappingExtensions
{
    /// <summary>
    /// Maps User to UserDto.
    /// </summary>
    public static UserDto? ToUserDto(this User? source)
    {
        if (source is null)
            return null;

        return new UserDto
        {
            Name = source.Name,
            Age = source.Age,
            Email = source.Email
        };
    }
}

Performance

Moedim.Mapper uses source generators to create mapping code at compile time, resulting in:

  • Zero reflection overhead - Direct property assignments
  • Similar to manual mapping - Generated code is as fast as hand-written code
  • No runtime initialization - Ready to use immediately
  • Optimized for collections - Efficient collection transformations

Run benchmarks with:

dotnet run --project tests/Moedim.Mapper.Performance.Tests -c Release

Advanced Features

Fluent Configuration Interface

The fluent configuration API provides a programmatic way to define mappings with full IntelliSense support and type safety.

Basic Fluent Mapping
using Moedim.Mapper;

var builder = new MapperConfigurationBuilder();

// Create a simple mapping
var mapping = builder.CreateMap<User, UserDto>();

// The mapping is now configured and ready to use
// Note: mapping.ForMember() returns IMappingExpression for chaining
Fluent Custom Property Mapping

Map properties with different names using the fluent API:

var builder = new MapperConfigurationBuilder();

builder.CreateMap<User, UserDto>()
    .ForMember(dest => dest.FullName, opt => opt.MapFrom(src => src.Name))
    .ForMember(dest => dest.EmailAddress, opt => opt.MapFrom(src => src.Email));

// Custom property mappings configured:
//   - FullName ← Name
//   - EmailAddress ← Email
Fluent Ignoring Properties

Exclude specific properties from mapping:

var builder = new MapperConfigurationBuilder();

builder.CreateMap<User, UserDto>()
    .ForMember(dest => dest.InternalId, opt => opt.Ignore())
    .ForMember(dest => dest.Metadata, opt => opt.Ignore());

// InternalId and Metadata will be excluded from mapping
Method Chaining

The fluent API supports method chaining for concise configuration:

var builder = new MapperConfigurationBuilder();

builder.CreateMap<Customer, CustomerDto>()
    .ForMember(dest => dest.CustomerName, opt => opt.MapFrom(src => src.Name))
    .ForMember(dest => dest.PrimaryEmail, opt => opt.MapFrom(src => src.Email))
    .ForMember(dest => dest.InternalId, opt => opt.Ignore())
    .ForMember(dest => dest.DisplayAddress, opt => opt.MapFrom(src => src.Address));
Multiple Mapping Configurations

Configure multiple type mappings in a single builder:

var builder = new MapperConfigurationBuilder();

builder.CreateMap<User, UserDto>()
    .ForMember(dest => dest.FullName, opt => opt.MapFrom(src => src.Name));

builder.CreateMap<Customer, CustomerDto>()
    .ForMember(dest => dest.CustomerName, opt => opt.MapFrom(src => src.Name));

builder.CreateMap<Order, OrderDto>()
    .ForMember(dest => dest.OrderNumber, opt => opt.MapFrom(src => src.Id));

// Multiple mappings are now configured in the builder
Fluent API Interfaces
public interface IMapperConfigurationBuilder
{
    IMappingExpression<TSource, TDestination> CreateMap<TSource, TDestination>();
}

public interface IMappingExpression<TSource, TDestination>
{
    IMappingExpression<TSource, TDestination> ForMember<TMember>(
        Expression<Func<TDestination, TMember>> destinationMember,
        Action<IMemberConfigurationExpression<TSource, TDestination, TMember>> memberOptions);
}

public interface IMemberConfigurationExpression<TSource, TDestination, TMember>
{
    void MapFrom(Expression<Func<TSource, TMember>> sourceMember);
    void Ignore();
}

Configuration Marker Attribute

Mark configuration classes for the source generator:

[MapperConfiguration]
public class MyMappingConfiguration
{
    public void Configure(IMapperConfigurationBuilder builder)
    {
        builder.CreateMap<User, UserDto>()
            .ForMember(dest => dest.FullName, opt => opt.MapFrom(src => src.Name));
    }
}

Supported Type Conversions

  • Direct type matches
  • Nullable value types (int to int?, etc.)
  • Collection types (Array, List, IEnumerable) with element transformation
  • Numeric conversions (int to long, etc.)
  • Nested complex objects (automatic recursive mapping)
  • Custom value converters (IValueConverter<TSource, TDest>)

Requirements

  • .NET 6.0, 7.0, or 8.0
  • C# 11 or later (for consuming projects)
  • Visual Studio 2022 or Rider (for best IDE support)

Project Structure

  • Moedim.Mapper - Core library with attributes and interfaces
  • Moedim.Mapper.SourceGenerator - Roslyn source generator implementation
  • Moedim.Mapper.Tests - Unit tests
  • Moedim.Mapper.SourceGenerator.Tests - Generator-specific tests
  • Moedim.Mapper.Performance.Tests - Performance benchmarks
  • Moedim.Mapper.Sample - Example usage

Migrating from AutoMapper

Moedim.Mapper provides both automated and manual migration options:

Automated Migration Tool

Install the migration tool globally:

dotnet tool install -g Moedim.Mapper.Migration.Tool

Analyze your project for AutoMapper usage:

moedim-mapper-migrate analyze path/to/YourProject.csproj --verbose

Migrate automatically to Moedim.Mapper:

moedim-mapper-migrate migrate path/to/YourProject.csproj

Generate a compatibility report:

moedim-mapper-migrate report path/to/YourProject.csproj --format html

For detailed tool documentation, see Migration Tool README.

Manual Migration

For step-by-step manual migration guidance with code comparisons, see MIGRATION_GUIDE.md.

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.

Acknowledgments

Built with:

Version History

1.0.0 (Initial Release)

  • Attribute-based mapping with [MapFrom] and [MapTo]
  • Custom property mapping with [MapProperty]
  • Property exclusion with [IgnoreProperty]
  • Collection mapping support with automatic item transformation
  • Nested object mapping - Automatic recursive mapping of complex objects
  • Value converters - Custom transformation with [ConvertWith] and IValueConverter<TSource, TDest>
  • Deep object graph mapping - Handle multi-level nested structures
  • Collection of complex objects - Map List<SourceType> to List<DestType>
  • Null-safe code generation
  • Multi-framework support (.NET 6.0, 7.0, 8.0)
Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  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 is compatible.  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 was computed.  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 was computed.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net6.0

    • No dependencies.
  • net7.0

    • No dependencies.
  • net8.0

    • No dependencies.

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
1.0.1 266 11/13/2025
1.0.0-preview.6 221 11/13/2025
1.0.0-preview.4 218 11/12/2025