mapperly Documentation

A .NET source generator for generating object mappings. No runtime reflection.

RAW Doc

File: README.md

Mapperly






Mapperly is a .NET source generator for generating object mappings.

Because Mapperly creates the mapping code at build time, there is minimal overhead at runtime.
Even better, the generated code is perfectly readable, allowing you to verify the generated mapping code easily.

Documentation

The documentation is available here.

Quickstart

Installation

Add the NuGet Package to your project:

bash
dotnet add package Riok.Mapperly

Create your first mapper

Create a mapper declaration as a partial class
and apply the Riok.Mapperly.Abstractions.MapperAttribute attribute.
Mapperly generates mapping method implementations for the defined mapping methods in the mapper.

c#
// Mapper declaration
[Mapper]
public partial class CarMapper
{
    public partial CarDto CarToCarDto(Car car);
}

// Mapper usage
var mapper = new CarMapper();
var car = new Car { NumberOfSeats = 10, ... };
var dto = mapper.CarToCarDto(car);
dto.NumberOfSeats.ShouldBe(10);

Read the docs for any further information.

Release Channels

Mapperly is distributed through two release channels:

  • Stable Channel: Production-ready releases with full support for the latest release. Install the latest stable version for production use. Subject to semantic versioning (breaking changes only in major version bumps).
  • Next Channel: Preview releases with upcoming features and improvements. These versions may contain breaking changes and are not subject to semantic versioning. Use for testing and early access to new features.

Support Policy

Only the latest version released on the stable channel is fully supported.
We strive to support all .NET versions that are currently supported by Microsoft.

Upgrading

Find a list of breaking changes for each major version and upgrade guides here.

Support

Need assistance with Mapperly or looking for consulting services? The riok team is available to help with:

  • Enterprise support and training
  • Architecture consultation
  • Custom feature development and integration support
  • Performance optimization and code reviews

Reach out through GitHub Discussions for questions, or contact the riok team directly for professional consulting services at [email protected].
You can also support the project and get priority support through GitHub Sponsors.

How To Contribute

We would love for you to contribute to Mapperly and help make it even better than it is today!
Find information on how to contribute in the docs.

License

Mapperly is Apache 2.0 licensed.


File: docs/docs/getting-started/faq.md


Frequently asked questions and answers {#faq}

Here you can find answers to frequently asked questions and common problems about Mapperly.

Mapperly does not work when I use source generator X.

Chaining source generators is not supported by Roslyn.

I updated the Mapperly version, but the generated code still looks the same.

Restart the IDE to make it load the new version of Mapperly. This is a bug of the IDE.

Everything is configured correctly and dotnet build works, but the IDE shows the error "[Mapper method] must have an implementation part because it has accessibility modifiers"

Make sure your project meets the requirements.
Try rebuilding the solution or restarting the IDE. This is a bug in the IDE.

My advanced use case isn't supported by Mapperly or needs lots of configuration. What should I do?

Write the mapping for that class manually. You can mix automatically generated mappings and user implemented mappings without problems.

My code throws `FileNotFoundException` with `Riok.Mapperly.Abstractions`. What should I do?

Are you using reference handling
or have you enabled the preservation of Mapperly attributes at runtime?
Make sure ExcludeAssets on the PackageReference does not include runtime as these features require runtime assets.

Can I get professional help or consulting for Mapperly?

Yes! The riok team offers professional consulting and support services for Mapperly.
Whether you need help with architecture consultation, implementation support, custom feature development the team is here to assist. Reach out through [email protected] for professional services. You can also support the project through GitHub Sponsors to get priority support.


File: docs/docs/getting-started/first-mapper.md


Create your first mapper

Create a mapper declaration as a partial class
and apply the Riok.Mapperly.Abstractions.MapperAttribute attribute.
Mapperly generates mapping method implementations for the defined mapping methods in the mapper.

csharp title="mapper declaration"
[Mapper]
public partial class CarMapper
{
    public partial CarDto CarToCarDto(Car car);
}
csharp title="mapper usage"
var mapper = new CarMapper();
var car = new Car { NumberOfSeats = 10, ... };
var dto = mapper.CarToCarDto(car);
dto.NumberOfSeats.ShouldBe(10);

File: docs/docs/getting-started/generated-mapper-example.mdx


import CodeBlock from '@theme/CodeBlock';
import CarSource from '!!raw-loader!../../src/data/generated/samples/Car.cs';
import CarDtoSource from '!!raw-loader!../../src/data/generated/samples/CarDto.cs';
import CarMapperSource from '!!raw-loader!../../src/data/generated/samples/CarMapper.cs';
import GeneratedCarMapperSource from '!!raw-loader!../../src/data/generated/samples/CarMapper.g.cs';

Generated mapper example

This example will show you what kind of code Mapperly generates.
It is based on the Mapperly sample.
To view the generated code of your own mapper, refer to the generated source configuration.

The source classes

In this example, we have a car class with some general information.

{CarSource}

The target classes

Our sample target classes are mostly the same as the source classes.
For demonstration purposes, we named the manufacturer property differently to show how Mapperly can handle this use case.

{CarDtoSource}

The mapper

The actual mapper is pretty simple. We use a static mapper class in this example.
As usual, we need to mark the mapper with the [Mapper] attribute, so that the source generator is able to recognize it.
Because the manufacturer/producer properties are named differently, we also need to configure that via an attribute.
In addition, because the CarColor and CarColorDto entries have different numeric values, we need to configure Mapperly to map them by their name.

{CarMapperSource}

The generated code

{GeneratedCarMapperSource}


File: docs/docs/getting-started/installation.mdx


import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
import Conditional from '@site/src/components/Conditional';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';

Installation

Requirements

  • .NET 8+ or .NET Framework 4.x.
  • C# language version 9 or later
  • Roslyn 4.8 or later

Add the NuGet Package to your project

All you need to do, to install Mapperly is to add a NuGet reference pointing to the package Riok.Mapperly.

{``}
bash
dotnet add package Riok.Mapperly

:::info
To remove the runtime dependency of Mapperly add
ExcludeAssets="runtime" and PrivateAssets="all"
to the resulting PackageReference.
:::

powershell
Install-Package Riok.Mapperly

:::info
To remove the runtime dependency of Mapperly add
ExcludeAssets="runtime" and PrivateAssets="all"
to the resulting PackageReference.
:::

:::warning
This is a prerelease version of Mapperly and is not subject to semantic release.
The API may break in any prerelease.
Checkout the latest stable version here.
:::

Release Channels

Mapperly is distributed through two release channels:

  • Stable Channel: Production-ready releases with full support for latest release. Install the latest stable version for production use. Subject to semantic versioning (breaking changes only in major version bumps).
  • Next Channel: Preview releases with upcoming features and improvements. These versions may contain breaking changes and are not subject to semantic versioning. Use for testing and early access to new features.

Support policy

Only the latest version released on the stable channel is fully supported.
We strive to support all .NET versions that are currently supported by Microsoft.

Preserving the attributes at runtime

Mapperly removes the attribute references at compile time by default (they have the ConditionalAttribute).
If you want to preserve the attribute references at runtime
you can set the MSBuild variable MapperlyAbstractionsScope to runtime.

xml
<MapperlyAbstractionsScope>runtime</MapperlyAbstractionsScope>

:::info
When preserving the attribute usages, the Mapperly package reference needs to include the runtime assets.
Make sure ExcludeAssets on the PackageReference does not include runtime.
:::

:::info
MapperlyAbstractionsScope only works in projects referencing Riok.Mapperly directly.
Alternatively you can set a constant MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME:

xml
<DefineConstants>$(DefineConstants);MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME</DefineConstants>

:::


File: docs/docs/contributing/architecture.md


Architecture

Mapperly is an incremental .NET source generator implementation.
Source generators are explained here
and here.

Solution

  • benchmarks Benchmarks to analyze the performance of the generated code and the source generator itself
  • build Build scripts
  • docs Documentation of Mapperly
  • samples Sample implementations of Mappers using Mapperly
  • src Source code of Mapperly
    • Riok.Mapperly The source generator implementation
    • Riok.Mapperly.Abstractions Abstractions and attributes to be used by the application code to configure Mapperly.
      This is referenced by the source generator but is not needed at runtime
      (except for some features, e.g., reference handling).
  • test Unit- and integration tests of Mapperly

Flow

This describes the process implemented by Mapperly on a higher level.
For each discovered MapperAttribute a new DescriptorBuilder is created.
The DescriptorBuilder is responsible to build a MapperDescriptor which holds all the mappings.
The DescriptorBuilder does this by following this process:

  1. Extracting the configuration from the attributes
  2. Extracting user implemented and user defined mapping methods.
    It instantiates a User*Mapping (eg. UserDefinedNewInstanceMethodMapping) for each discovered mapping method and adds it to the queue of mappings to work on.
  3. Extracting user implemented object factories
  4. Extracting user implemented format providers
  5. Extracting external mappings
  6. For each mapping in the queue the DescriptorBuilder tries to build its implementation bodies.
    This is done by a so called *MappingBodyBuilder.
    A mapping body builder tries to map each property from the source to the target.
    To do this, it asks the DescriptorBuilder to create mappings for the according types.
    To create a mapping from one type to another, the DescriptorBuilder loops through a set of *MappingBuilders.
    Each of the mapping builders try to create a mapping (an ITypeMapping implementation) for the asked type mapping by using
    one approach on how to map types (eg. an explicit cast is implemented by the ExplicitCastMappingBuilder).
    These mappings are queued in the queue of mappings which need the body to be built.
  7. The SourceEmitter emits the code described by the MapperDescriptor and all its mappings.
    The syntax objects are created by using SyntaxFactory and SyntaxFactoryHelper.
    The SyntaxFactoryHelper tries to simplify creating formatted syntax trees.
    If indentation is needed,
    the SyntaxFactoryHelper instance of the SourceEmitterContext/TypeMappingBuildContext can be used.

Roslyn multi targeting

Mapperly targets multiple Roslyn versions by building multiple NuGet packages
and merging them together into a single one.
Multi-targeting is needed to support new language features,
such as required members introduced in C# 11,
while still supporting older compiler versions.

See build/package.sh for details.

To introduce support for a new roslyn version see common tasks.

C# language features

The Mapperly source generator targets .netstandard2.0 but uses the latest C# language version
and polyfills generated by Meziantou.Polyfill.
Some newer C# language features require new runtime features and therefore cannot be used in Mapperly.
A good C# language features requirements overview is available here.


File: docs/docs/contributing/common-tasks.md


Common tasks

This page provides todo lists for some common tasks one may encounter while contributing to Mapperly.

New diagnostic

To introduce a new diagnostic follow these steps:

  1. Ensure no existing diagnostic in Riok.Mapperly.Diagnostics.DiagnosticDescriptors.cs matches the new use case.
  2. Create a new public static readonly entry following the existing naming and formatting style,
    as ID use RMG<NUMBER> with <NUMBER> being the highest already used number plus one.
    The highest used number can be found in AnalyzerReleases.Shipped.md as there may be removed diagnostics which are not present in DiagnosticDescriptors anymore
    but which should still not be used for new diagnostics.
  3. Add the new diagnostic to AnalyzerReleases.Shipped.md (Mapperly does not use the Unshipped file).
  4. Add a new documentation file at docs/docs/configuration/analyzer-diagnostics/{id}.mdx (as needed)
  5. Add a unit test generating and asserting the added diagnostic (use TestHelperOptions.AllowDiagnostics and Should().HaveDiagnostic(...).HaveAssertedAllDiagnostics().

It is not necessary to update the analyzer-diagnostics/index.mdx documentation file manually,
as it is generated automatically on the basis of the AnalyzerReleases.Shipped.md file.

New public API

If new public API surface is introduced in Riok.Mapperly.Abstractions or existing API surface is modified,
the PublicApiTest snapshot test needs to be updated.
See also VerifyTests.

Add support for a new roslyn version

To support a new roslyn version via multi targeting follow these steps (see also architecture/roslyn multi targeting):

  1. Include the new version in roslyn_versions in build/package.sh.
  2. Create a new file Riok.Mapperly.Roslyn$(Version).props in src/Riok.Mapperly similar to the existing ones
    and define constants and include dependencies as needed.
  3. Update the default ROSLYN_VERSION in src/Riok.Mapperly/Riok.Mapperly.csproj.
  4. Update the Microsoft.CodeAnalysis.CSharp dependency version.
  5. Adjust the .NET version matrix of the integration-test GitHub Actions job (defined in .github/workflows/test.yml)
    to include a dotnet version which is based on the added Roslyn version.
  6. Adjust the .NET version in the global.json file as needed.
  7. Add the new version in Riok.Mapperly.IntegrationTests.Helpers.Versions and Riok.Mapperly.IntegrationTests.BaseMapperTest.GetCurrentVersion.
  8. If generated code changes based on the new Roslyn version,
    adjust the VersionedSnapshotAttributes as needed.
  9. Adjust the documentation as needed.
  10. Add new preprocessor constants to .csharpierrc.yaml.
  11. Add new GitHub required checks as needed (needs to be done by a maintainer).

Mapping syntax

Mapperly Mappings use Roslyn syntax trees.
RoslynQuoter and SharpLab
are fantastic tools to understand and work with Roslyn syntax trees.
The Riok.Mapperly.Emit.Syntax.SyntaxFactoryHelper and Microsoft.CodeAnalysis.CSharp.SyntaxFactory classes help building these syntax trees.


File: docs/docs/contributing/debugging.md


Debugging

Generated code

To debug the code generated by Mapperly just set a breakpoint in the generated code and you are ready to go.
Check here on how to inspect the generated code.

Unit tests

To debug the Mapperly in unit tests, set a breakpoint
in the code of Mapperly which you want to debug and run the tests in debug mode.

Integration tests

If your IDE supports the DebugRoslynComponent launch configuration command,
you can set your breakpoints and debug the preconfigured IntegrationTests profile which will debug Mapperly
in the context of the integration tests.
JetBrains Rider and Visual Studio both support DebugRoslynComponent.
Visual Studio requires the Roslyn SDK to be installed.

If your IDE does not support DebugRoslynComponent,
debugging the integration tests requires some more effort.
Mapperly uses a compile constant DEBUG_SOURCE_GENERATOR to determine whether to attach a debugger.
If it is set, it tries to attach a debugger
(it uses Debugger.Launch() on windows, on other operating systems it tries to launch JetBrains Rider)
and waits for the connection of the debugger (for up to 30 seconds).
If the automatic debugger attachment fails you can use these 30 seconds to attach the debugger manually
(for Visual Studio see attach to running processes).
You can use the DefineConstants dotnet build or csproj parameter to define the DEBUG_SOURCE_GENERATOR constant.

For the debugger attachment with JetBrains Rider to work correctly,
Generate Shell Scripts needs to be enabled in the JetBrains ToolBox
and the generated shell scripts need to be on the path (Mapperly calls rider attach-to-process {pid} {Mapperly-Sln-File}).

CSC warning CS8785 stack trace

If you see the compiler warning CS8785: Generator 'MapperlyGenerator' failed to generate source,
you can get a stack trace from the C# compiler by emitting a compiler error log. The stack trace is included in the SARIF output.

Add the following MSBuild property when you build:

  • /p:ErrorLog=compiler-diagnostics.sarif

Then open the SARIF file to inspect the generator exception and stack trace.


File: docs/docs/contributing/docs.md


Documentation

The Mapperly documentation is built using Docusaurus
and is located in the docs directory.

Dependencies

Docusaurus is based on the node ecosystem.
A .node-version file provides the node version number to be used.
This file can be interpreted by several node version managers (eg. fnm or nvm).
Make sure you use a node version matching the version in .node-version for all following commands.

To install documentation dependencies run:

bash
npm i

Update generated documentation

To build the generated parts of the documentation
(eg. the API documentation (located in docs/99-api), the table of analyzer rules, the samples, ...)
ensure the dotnet solution is built or run dotnet build in the solutions root directory.
Then run the prebuild script:

bash
npm run prebuild

To reflect changes, this command needs to be re-run each time the source of the generated output changes.

Local Development

bash
npm run start

This command starts a local development server and opens up a browser window.
Most changes are reflected live without having to restart the server.
The documentation search does not work when using the local development server,
as it depends on the statically built content (see build).

Linting

To run the linters locally run:

bash
npm run lint

To fix detected issues automatically run:

bash
npm run fix

Build

bash
npm run build

This command generates static content into the build directory
and can be served using any static contents hosting service or locally using npm run serve.


File: docs/docs/contributing/index.md


Contributing

We would love for you to contribute to Mapperly and help make it even better than it is today!
As a contributor, here are the guidelines we would like you to follow.

Code of Conduct

Help us keep Mapperly open and inclusive. Please read and follow our code of conduct.

Got a question or problem?

If you have a question or a problem create a new GitHub Discussion.

Found a Bug?

If you find a bug in the source code, you can help us by submitting a GitHub Issue.
Even better, you can submit a Pull Request with a fix.

Missing a Feature?

You can request a new feature by submitting an issue to our GitHub repository.
If you would like to implement a new feature, please consider the size of the change in order to determine the right steps to proceed:

  • For a major feature, first open an issue and outline your proposal so that it can be discussed.
    Getting early feedback will help ensure your implementation work is accepted by the maintainers.
    This will also allow us to better coordinate our efforts and minimize duplicated effort.

    Note: Adding a new topic to the documentation, or significantly re-writing a topic, counts as a major feature.

  • Small Features can be crafted and directly submitted as a Pull Request.

Using AI tools

You are welcome to use AI tools to help you contribute to Mapperly.
However, you are responsible for every line of code you submit.
Before opening a pull request, please make sure that you did not use AI tools to generate the PR,
or that you have manually verified that you understand the code, that it is correct, optimal, and follows the project guidelines and architecture.

Low-quality, AI-generated pull requests that have not been reviewed and understood by their author
will be closed immediately without further explanation.

Submission Guidelines

Submitting an issue

Before you submit an issue, please search the issue tracker and discussions.
An issue for your problem might already exist and the discussion might inform you of workarounds readily available.

You can file new issues by filling out the new issue template.

Submitting a pull request (PR)

Before you submit your Pull Request (PR) consider the following guidelines:

  1. Search GitHub for an open or closed PR that relates to your submission.
    You don't want to duplicate existing efforts.

  2. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add.
    Discussing the design upfront helps to ensure that we're ready to accept your work.

  3. Get an overview on how Mapperly works by reading this contributing documentation, the architectural overview and related documentation.

  4. Fork the riok/mapperly repository.

  5. In your forked repository, make your changes in a new Git branch:

    shell
    git checkout -b my-fix-branch main
  6. Create your patch, including appropriate test cases and documentation updates.

  7. Commit your changes using a descriptive commit message.
    Adherence to these conventions is necessary because release notes are automatically generated from these messages.
    Husky and csharpier automatically format changed files when commited.

    shell
    git commit --all

    Note: the optional commit -a command-line option will automatically "add" and "rm" edited files.

  8. If any commits have been made to the upstream main branch,
    you should rebase your development branch so that merging it will be a simple fast-forward that won't require any conflict resolution work.

  9. shell
    git checkout main
    git pull upstream main
    git checkout my-fix-branch
    git rebase main
  10. Now, it may be desirable to squash some of your smaller commits down into a small number of larger more cohesive commits. You can do this with an interactive rebase:

    bash
    # Rebase all commits on your development branch
    git checkout
    git rebase -i main
  11. Push your branch to GitHub:

    shell
    git push origin my-fix-branch
  12. In GitHub, send a pull request to riok:main and request a review from a maintainer.

Checks failure

Once you've submitted a pull request, all continuous-integration checks are triggered.
If some of these checks fail, it could be either problems with the pull request or a failure of some test cases.
For more information on the failure, check the output logs of the jobs.

Reviewing a pull request

The reviewers will provide you feedback and approve your changes as soon as they are satisfied.
If we ask you for changes in the code, you can follow the GitHub Guide to incorporate feedback in your pull request.