Chd.Mapping.Roslyn.Advanced 8.1.6

dotnet add package Chd.Mapping.Roslyn.Advanced --version 8.1.6
                    
NuGet\Install-Package Chd.Mapping.Roslyn.Advanced -Version 8.1.6
                    
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="Chd.Mapping.Roslyn.Advanced" Version="8.1.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Chd.Mapping.Roslyn.Advanced" Version="8.1.6" />
                    
Directory.Packages.props
<PackageReference Include="Chd.Mapping.Roslyn.Advanced" />
                    
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 Chd.Mapping.Roslyn.Advanced --version 8.1.6
                    
#r "nuget: Chd.Mapping.Roslyn.Advanced, 8.1.6"
                    
#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 Chd.Mapping.Roslyn.Advanced@8.1.6
                    
#: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=Chd.Mapping.Roslyn.Advanced&version=8.1.6
                    
Install as a Cake Addin
#tool nuget:?package=Chd.Mapping.Roslyn.Advanced&version=8.1.6
                    
Install as a Cake Tool

Chd.Mapping.Roslyn.Advanced – High-Performance, Expression-Aware DTO Mapper

NuGet License Downloads

Chd (Cleverly Handle Difficulty) library helps you cleverly handle difficulty, write code quickly, and keep your application stable.

Chd.Mapping.Roslyn.Advanced is a blazing-fast, compile-time object mapper for .NET with expression-based mapping supportβ€”built on Roslyn source generators for maximum performance and type safety!


πŸ“‘ Table of Contents

  1. About
  2. Why This Package?
  3. Features
  4. Installation
  5. Quick Start
  6. Usage Examples
  7. IDE Experience
  8. Performance
  9. Comparison with Other Mappers
  10. Best Practices
  11. Limitations
  12. Troubleshooting
  13. Viewing Generated Code
  14. FAQ
  15. Examples Repository
  16. Contributing
  17. Authors
  18. Acknowledgments

🧐 About

Chd.Mapping.Roslyn.Advanced is a modern, compile-time object mapper for .NET, built with Roslyn source generators. It automatically creates fast, type-safe, and debuggable mapping code between DTOs and entities during your build processβ€”eliminating runtime reflection, configuration headaches, and mapping errors typical of tools like AutoMapper or Mapster.

This advanced version supports not only property-to-property mapping, but also expression-based mapping for calculated or derived properties, nested object mapping, and high-performance scenarios.


πŸš€ Why this package

  • Zero Reflection, Maximum Speed: All mapping logic is generated at build time. No runtime cost, no dynamic code, no hidden performance penalty.
  • Type Safety: All mapping is verified at buildβ€”if your DTO or entity changes, mapping errors become compiler errors, not runtime bugs.
  • Debuggability: All generated mapping code is standard C#, fully visible in your IDE. Set breakpoints, watch variables, and step through mappings with the debugger.
  • Simplicity: No configuration files, no dependency injection, no startup scanning, no magic. Just add attributes, build, and use.
  • Advanced Scenarios: Supports custom expressions, nested objects, collections, and property name mismatches with a single attribute.

✨ Features

  • Attribute-based mapping: [MapTo], [MapProperty] (with expression support)
  • Compile-time generation of implicit mapping operators
  • Expression-based property mapping (e.g., [MapProperty("Price + Tax - Discount")])
  • No runtime logic. Just generated code: (e.g., NetTotal = entity.Price + entity.Tax - entity.Discount;)
  • Nested and collection mapping support
  • No runtime dependency, no reflection, no configuration files
  • Full support for .NET 8+, .NET 9, and .NET Standard 2.0 SDK-style projects
  • Live IntelliSense inside [MapProperty("…")] β€” counterpart property names are suggested while you type
  • Roslyn analyzers for instant feedback in the editor:
    • MAP001 β€” class decorated with [MapTo] is missing the partial modifier (ships with a one-click code fix)
    • MAP002 β€” unknown identifier inside [MapProperty("…")], reported on the attribute itself
  • Defensive generator β€” when an expression contains an unknown identifier, that single line is omitted from the generated operator instead of breaking the build inside *.g.cs
  • Global-namespace classes are fully supported (no namespace <global namespace> block is emitted)

πŸ“¦ Installation

Install via .NET CLI:

dotnet add package Chd.Mapping.Roslyn.Advanced

Or via NuGet Package Manager Console:

Install-Package Chd.Mapping.Roslyn.Advanced

Or via Package Manager UI in Visual Studio / Rider.

Requirements:

  • .NET Standard 2.0+
  • .NET Core 3.1+
  • .NET 5, 6, 7, 8, 9+
  • C# 8.0 or higher

πŸš€ Quick Start


using Chd.Mapping.Abstractions;
namespace ChdRoslynMappingTest
{
    // 1. Mark your DTO with [MapTo] and expression mapping
    [MapTo(typeof(OrderEntity))]
    public partial class OrderDto
    {
        public decimal Price { get; set; }
        public decimal Tax { get; set; }
        public decimal Discount { get; set; }

        // Expression-based calculated property!
        [MapProperty("Price * (Tax + 100) / 100 - Discount")]
        public decimal NetTotal { get; set; }
    }

    // 2. Define your Entity (must be partial)
    public partial class OrderEntity
    {
        public decimal Price { get; set; }
        public decimal Tax { get; set; }
        public decimal Discount { get; set; }
        public decimal NetTotal { get; set; }
}
     // 3. Use implicit operators - that's it!
    internal class Program
    {
        static void Main(string[] args)
        {
            var dto = new OrderDto { Price = 100, Tax = 18, Discount = 2 };
            OrderEntity entity = dto;  // DTO β†’ Entity with calculation!
            Console.WriteLine($"NetTotal: {entity.NetTotal}");  // Output: 116
        }
    }
}

Output:

NetTotal: 116

βœ… Expression-based mapping! βœ… No reflection overhead! βœ… Full IntelliSense support! βœ… Compile-time validation!


πŸ’‘ Usage Examples

1. Basic Mapping

Scenario: Simple DTO ↔ Entity mapping with identical property names.

using Chd.Mapping.Abstractions;
using System;

[MapTo(typeof(UserEntity))]
public partial class UserDto
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

public partial class UserEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

class Program
{
    static void Main()
    {
        // DTO to Entity
        var dto = new UserDto 
        { 
            Id = 1, 
            Name = "Mehmet",
            Email = "mehmet@example.com"
        };

        UserEntity entity = dto;  // Implicit mapping
        Console.WriteLine($"Entity: {entity.Id}, {entity.Name}, {entity.Email}");

        // Entity back to DTO
        UserDto dto2 = entity;
        Console.WriteLine($"DTO: {dto2.Id}, {dto2.Name}, {dto2.Email}");
    }
}

Console Output:

Entity: 1, Mehmet, mehmet@example.com
DTO: 1, Mehmet, mehmet@example.com

2. Advanced Property Mapping

Scenario: Map properties with different names.

using Chd.Mapping.Abstractions;
using System;

[MapTo(typeof(UserEntity))]
public partial class UserDto
{
    public int Id { get; set; }

    [MapProperty("FullName")]  // Maps DTO.Name β†’ Entity.FullName
    public string Name { get; set; }

    [MapProperty("EmailAddress")]  // Maps DTO.Email β†’ Entity.EmailAddress
    public string Email { get; set; }
}

public partial class UserEntity
{
    public int Id { get; set; }
    public string FullName { get; set; }
    public string EmailAddress { get; set; }
}

class Program
{
    static void Main()
    {
        var dto = new UserDto 
        { 
            Id = 42, 
            Name = "Mehmet Yoldaş",
            Email = "mehmet@example.com"
        };

        UserEntity entity = dto;

        Console.WriteLine($"Id: {entity.Id}");
        Console.WriteLine($"FullName: {entity.FullName}");
        Console.WriteLine($"EmailAddress: {entity.EmailAddress}");
    }
}

Console Output:

Id: 42
FullName: Mehmet Yoldaş
EmailAddress: mehmet@example.com

Expression-Based Mapping

πŸ”₯ The Power of Advanced Mapping: Calculate derived properties with C# expressions!

using Chd.Mapping.Abstractions;
using System;

[MapTo(typeof(OrderEntity))]
public partial class OrderDto
{
    public decimal Price { get; set; }
    public decimal Tax { get; set; }
    public decimal Discount { get; set; }

    // Mathematical expression
    [MapProperty("Price * (Tax + 100) / 100 - Discount")]
    public decimal NetTotal { get; set; }

    public string Name { get; set; }
    public string Surname { get; set; }

    // String concatenation with single quotes
    [MapProperty("Name + ' ' + Surname")]
    public string FullName { get; set; }

    public bool IsActive { get; set; }

    // Ternary operator
    [MapProperty("IsActive ? 'Active' : 'Passive'")]
    public string StatusText { get; set; }
}

public partial class OrderEntity
{
    public decimal Price { get; set; }
    public decimal Tax { get; set; }
    public decimal Discount { get; set; }
    public decimal NetTotal { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
    public string FullName { get; set; }
    public bool IsActive { get; set; }
    public string StatusText { get; set; }
}

class Program
{
   static void Main()
   {
      var dto = new OrderDto 
      { 
          Price = 100, 
          Tax = 18, 
          Discount = 2, 
          Name = "Mehmet", 
          Surname = "Yoldaş", 
          IsActive = true 
      };

      OrderEntity entity = dto;  // Expression-based mapping!

      Console.WriteLine($"NetTotal: {entity.NetTotal}");           // 116
      Console.WriteLine($"FullName: {entity.FullName}");           // Mehmet Yoldaş
      Console.WriteLine($"StatusText: {entity.StatusText}");       // Active

      // Reverse mapping works too!
      OrderDto dto2 = entity;
      Console.WriteLine($"DTO NetTotal: {dto2.NetTotal}");         // 116
   }
}

Console Output:

NetTotal: 116
FullName: Mehmet Yoldaş
StatusText: Active
DTO NetTotal: 116

πŸ’‘ Pro Tip:
You can use either single quotes (' ') or double quotes (" ") for string literals in expressions.
Recommended style: Single quotes ([MapProperty("Name + ' ' + Surname")]) for better readability.

Supported Expression Types:

  • βœ… Mathematical operations: +, -, *, /, %
  • βœ… Comparison: ==, !=, <, >, <=, >=
  • βœ… Logical: &&, ||, !
  • βœ… Ternary operator: condition ? true : false
  • βœ… String concatenation: +
  • βœ… Property access: Property.SubProperty
  • βœ… Method calls: Property.ToString()
  • βœ… Parentheses for grouping: (A + B) * C

Generated Code Example:

The generator creates highly optimized code like this:

// <auto-generated />
public partial class OrderEntity
{
    public static implicit operator OrderDto(OrderEntity entity)
    {
        if(entity == null) return null;
        return new OrderDto 
        {
            Price = entity.Price,
            Tax = entity.Tax,
            Discount = entity.Discount,
            NetTotal = entity.Price * (entity.Tax + 100) / 100 - entity.Discount,
            Name = entity.Name,
            Surname = entity.Surname,
            FullName = entity.Name + " " + entity.Surname,
            IsActive = entity.IsActive,
            StatusText = entity.IsActive ? "Active" : "Passive"
        };
    }
}

βœ… Zero reflection - just pure, optimized C# code!
βœ… Fully debuggable - set breakpoints and step through!
βœ… Type-safe - compile-time validation!


3. Nested and Collection Mapping

Scenario: Automatically map nested objects and collections.

using Chd.Mapping.Abstractions;
using System;
using System.Collections.Generic;

[MapTo(typeof(ProductEntity))]
public partial class ProductDto
{
    public string Code { get; set; }
    public int Quantity { get; set; }
    public decimal Price { get; set; }
}

public partial class ProductEntity
{
    public string Code { get; set; }
    public int Quantity { get; set; }
    public decimal Price { get; set; }
}

[MapTo(typeof(OrderEntity))]
public partial class OrderDto
{
    public int OrderId { get; set; }
    public string CustomerName { get; set; }
    public List<ProductDto> Products { get; set; }
}

public partial class OrderEntity
{
    public int OrderId { get; set; }
    public string CustomerName { get; set; }
    public List<ProductEntity> Products { get; set; }
}

class Program
{
    static void Main()
    {
        var dto = new OrderDto
        {
            OrderId = 3,
            CustomerName = "Acme Corp",
            Products = new List<ProductDto>
            {
                new ProductDto { Code = "A123", Quantity = 2, Price = 49.99m },
                new ProductDto { Code = "B456", Quantity = 5, Price = 29.99m }
            }
        };

        // Automatic nested collection mapping!
        OrderEntity entity = dto; 

        Console.WriteLine($"Order #{entity.OrderId} - {entity.CustomerName}");
        Console.WriteLine("Products:");
        foreach (var p in entity.Products)
            Console.WriteLine($"  {p.Code}: {p.Quantity}x @ ${p.Price}");
    }
}

Console Output:

Order #3 - Acme Corp
Products:
  A123: 2x @ $49.99
  B456: 5x @ $29.99

βœ… Nested objects mapped automatically!
βœ… Collections supported: List<T>, IEnumerable<T>, arrays, etc.
βœ… Deep nesting works recursively!


4. Debugging and Maintainability

All mapping code is standard C# and fully debuggable:

  1. View Generated Files:

    • Visual Studio: Solution Explorer β†’ Dependencies β†’ Analyzers β†’ Chd.Mapping.Roslyn.Advanced
    • Or check obj/Debug/netX.X/generated/ folder
  2. Set Breakpoints:

    • Navigate to generated implicit operator methods
    • Step through mapping code line by line
    • Inspect variable values
  3. IntelliSense Support:

    • Full code completion
    • Navigate to definition (F12)
    • Find all references
    • Refactoring support

Example Generated Code:

public static implicit operator UserEntity(UserDto source)
{
    if (source == null) return null;
    return new UserEntity
    {
        Id = source.Id,
        FullName = source.Name,
        EmailAddress = source.Email
    };
}

Generated Code is:

  • βœ… Readable and maintainable
  • βœ… Optimized by the C# compiler
  • βœ… No runtime overhead
  • βœ… Type-safe at compile time

Tip β€” making generated files visible on disk: add the following to the consuming project so the generated .g.cs files are written next to your source and are clickable from Error List:

<PropertyGroup>
  <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
  <CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
</PropertyGroup>

<ItemGroup>
  <Compile Remove="Generated\**" />
</ItemGroup>

After the next build, generated mapping code lives under Generated/Chd.Mapping.Roslyn.Advanced/MappingGenerator/… and …/CollectionMappingGenerator/….


πŸ›  IDE Experience

In addition to the source generator, the package ships with a Roslyn CompletionProvider, two DiagnosticAnalyzers and one CodeFixProvider, so most mapping mistakes become editor errors before you even build.

IntelliSense inside [MapProperty("…")]

Place the caret between the quotes of [MapProperty("|")], press Ctrl+Space, and the IDE lists the public instance properties of the counterpart class (the one referenced by [MapTo(typeof(...))]). The same names are valid inside expressions such as Name + ' ' + Surname or IsActive ? 'Active' : 'Passive', because the generator rewrites those identifiers as entity.Name, entity.Surname, entity.IsActive etc., where entity is of the counterpart type.

Analyzer Diagnostics

ID Severity What it catches
MAP001 Error A class decorated with [MapTo] is missing the partial modifier. Ships with a code fix.
MAP002 Error An identifier used inside a [MapProperty("…")] expression is not a public property of the counterpart type. The squiggle points at the exact word inside the attribute string.

MAP002 example:

[MapTo(typeof(OrderEntity))]
public partial class OrderDto
{
    // OrderEntity has no "Surnam" property.
    [MapProperty("Name + ' ' + Surnam")]   // ← red squiggle on Surnam
    public string FullName { get; set; }
}

MAP002: 'Surnam' is not a public property of 'OrderEntity'. The expression in [MapProperty] must reference properties of the counterpart type.

Double-clicking the entry in Error List navigates straight to the typo β€” not to a *.g.cs file. Identifiers inside single-quoted segments (e.g. the Active / Passive in "IsActive ? 'Active' : 'Passive'") and C# reserved tokens (true, false, null, …) are not flagged.

When MAP002 triggers, the generator also defensively skips that single map line, so the build does not break inside generated files; you only see one clear error on the attribute. Fix the typo and the line reappears in the next build.

Code Fix: Add partial Modifier

MAP001 is paired with MapToMustBePartialCodeFixProvider. Place the caret on the squiggled class name, press Ctrl+. (Quick Actions) and choose Add 'partial' modifier β€” Roslyn rewrites the declaration in place:

// Before
[MapTo(typeof(Entity))]
public class MyDto { ... }

// After (one click)
[MapTo(typeof(Entity))]
public partial class MyDto { ... }

🚦 Performance

Scenario AutoMapper (ms) Mapster (ms) Chd.Mapping.Roslyn.Advanced (ms)
1,000,000 DTO→Entity mappings 980 410 180
100,000 Entity→DTO mappings w/nesting 180 74 34
Flat object, assign all properties 22 10 4

Source: Internal benchmarks, see /benchmarks.

Key Takeaway:
Chd.Mapping.Roslyn.Advanced mapping is as fast as hand-written code, and dramatically faster than reflection-based mappers.


βš– Comparison with Other Mappers

Capability AutoMapper Mapster Chd.Mapping.Roslyn.Advanced
Mapping time Runtime (reflection / IL emit) Runtime + optional codegen Compile time only
Configuration / profiles Required Optional None β€” attributes only
Startup scanning Yes Yes (when reflection mode) No
Debuggable mapping code ❌ (IL or expression trees) Partial βœ… Plain C# you can step through
Expression-based derived properties Via .ForMember(... => Expression) Via .Map(...) config βœ… [MapProperty("Price + Tax")]
Nested objects βœ… βœ… βœ…
Collections βœ… βœ… βœ…
Compile-time validation ❌ (runtime) Partial (codegen mode) βœ… C# compiler + MAP001 / MAP002
IntelliSense in mapping config ❌ ❌ βœ… CompletionProvider inside [MapProperty]
Analyzer for typos ❌ ❌ βœ… MAP002 on the attribute
Quick fix for missing partial n/a n/a βœ… MAP001 + Code Fix
Reflection at runtime Yes Yes (default) Never
Cold start cost High Medium Zero
Trim / AOT friendly ⚠ Partial ⚠ Partial βœ… Yes β€” no reflection
External runtime DLL AutoMapper.dll Mapster.dll None β€” only Chd.Mapping.Abstractions (attributes)

The differentiator of Chd.Mapping.Roslyn.Advanced is not raw speed alone (any compile-time mapper is fast); it is the end-to-end IDE experience β€” completion, analyzers and code fixes all ship in the same package.


πŸ† Best Practices and Tips

  • Mark all mapped classes as partial.
  • Use [MapTo(typeof(TargetType))] on your DTOs.
  • Use [MapProperty("...")] for calculated or custom-mapped properties.
  • Run a build to regenerate mapping code after model changes.
  • No Fody, no config files, no runtime setup required.

⚠ Limitations

  • [MapProperty] attribute only supports a single string parameter: use either the target property name or a full C# expression.
  • Expressions must be valid C# and use property names exactly as declared.
  • Nested mapping works if types on both sides also have [MapTo] or compatible structure.
  • Custom type conversions outside of C# syntax, external services, complex global mapping config are not supported.
  • Circular object graph mapping is not supported.
  • Classes marked with [MapTo] must be declared as partial.
    • If you forget to add partial, you’ll see a build-time diagnostic error (MAP001):
      "Class 'X' is marked with [MapTo] and must be declared as partial"
    • Visual Studio, Rider, and similar IDEs provide a quick fix (lightbulb action) for this β€” β€œAdd 'partial' modifier”.
    • With a single click, the partial keyword is automatically added to your class declaration:
      // Incorrect:
      [MapTo(typeof(Entity))]
      public class MyDto { ... }
      
      // Fixed:
      [MapTo(typeof(Entity))]
      public partial class MyDto { ... }
      

πŸš‘ Troubleshooting

  • MAP001: Class must be partial
    • Solution: Add partial to your class declaration (e.g., public partial class ...).
    • Your IDE (VS, Rider) typically offers a quick fix ("Add 'partial' modifier"/lightbulb) for this – just click to apply the fix!
  • MAP002: '<name>' is not a public property of '<Counterpart>'
    • Solution: fix the spelling, add the missing property to the counterpart type, or remove the [MapProperty] if it is no longer needed. Identifiers inside single-quoted segments (e.g. 'Active') and reserved tokens (true, false, null) are intentionally not flagged.
  • Build error inside *.g.cs: Should not happen anymore β€” MAP002 is emitted instead of broken generated code, and the offending line is omitted from the operator. If you still see one, please file an issue with the failing snippet.
  • IntelliSense suggestions do not appear inside [MapProperty("")]: Make sure your IDE uses Roslyn β‰₯ 4.8 (Visual Studio 2022 17.8+, Rider 2024.1+), and reload the consuming project (Solution Explorer β†’ right-click β†’ Unload / Reload) so the IDE picks up the analyzer DLL.
  • Squiggle remains after the issue is fixed: Roslyn caches diagnostics aggressively. Touch the file (add and remove a space) or Build β†’ Clean + Rebuild. As a last resort, close the solution and clear the IDE component-model cache.
  • Build error after changing models: Run a clean build (dotnet clean && dotnet build). Check generated sources in /obj (or in your Generated/ folder if EmitCompilerGeneratedFiles is enabled).
  • Attribute typo or syntax error: Ensure [MapTo] and [MapProperty] are spelled correctly, and property names are valid.
  • Expression compile errors: Expressions must be valid C#. If an error occurs, check the generated code and fix the expression.

πŸ” Viewing the Generated Mapping Code

  • Open /obj/Debug/net*/*Mapping*.g.cs (or your IDE’s generated files node) to inspect all mapping operator code.
  • You can debug into the generated code just like any hand-written C#.

❓ FAQ

Q: Do I need to write any mapping code?
A: No. Just add attributes and build – all mapping code is generated for you.

Q: Can I debug the generated code?
A: Yes! All generated code is standard C# and fully debuggable.

Q: Is there any runtime dependency?
A: No. All mapping logic is generated at compile time.

Q: What if I change my DTO or Entity?
A: The generator will update mappings on the next build. Mapping errors become compiler errors.


🀝 Contributing

Contributions, issues and feature requests are welcome!
Check issues page or submit a pull request.

πŸ§ͺ Examples Repository

This repository contains examples of how to use the Library.Mapping package in different scenarios, including DTO-Entity mapping, operator injection, and performance benchmarks.

πŸ‘€ Authors

πŸŽ‰ Acknowledgments

  • Thanks to all contributors and users of the CHD library ecosystem.
  • Inspired by best practices in .NET library design.

For issues, feature requests, or contributions, please visit the GitHub repository

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

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
8.1.6 92 6/17/2026
8.1.5 102 6/16/2026
8.1.4 95 6/13/2026
8.1.3 132 2/28/2026
8.1.2 116 2/19/2026
8.1.1 293 1/28/2026
8.0.9 124 1/23/2026
8.0.7 129 1/21/2026
8.0.6 125 1/20/2026
8.0.5 119 1/20/2026
8.0.4 119 1/20/2026
8.0.3 122 1/20/2026
8.0.2 124 1/20/2026
8.0.1 121 1/19/2026