120 lines
4.2 KiB
Markdown
120 lines
4.2 KiB
Markdown
# Mapper Source Generator Example
|
|
|
|
This project demonstrates how you can use incremental source generators in C# to generate domain transfer objects (DTOs)
|
|
and mapping classes from a POCO.
|
|
|
|
> [!WARNING]
|
|
> This is a sample project and not intended for production use. It is meant to illustrate the concept of using source generators.
|
|
> Please look into using either [mapster's](https://github.com/MapsterMapper/Mapster) code generator or [mapperly](https://github.com/riok/mapperly) instead.
|
|
|
|
## Requirements
|
|
- .NET 8.0 SDK
|
|
|
|
## What's Included
|
|
- `MapperSourceGen`: The main project that contains the attributes for decorating classes and properties for the source generator.
|
|
- `MapperSourceGen.Sample`: A sample class library that demonstrates the use of the source generator.
|
|
- `MapperSourceGen.SourceGenerator`: The source generator that generates DTOs and mapping classes.
|
|
- `MapperSourceGen.SourceGenerator.Tests`: Unit tests for the source generator to ensure it works as expected (snapshot testing).
|
|
|
|
Feel free to inspect the `.csproj` files for the projects to see how they are set up and how the source generator is integrated.
|
|
|
|
Additionally, a `launchSettings.json` file is included so that you can run the source generator in debug mode and step through the code. Simply execute `Source Generator`.
|
|
|
|
> [!NOTE]
|
|
> The source files emitted by the source generator will be under `Dependencies -> .NET 8.0 -> Source Generators` in the `MapperSourceGen.Sample` project if using Rider.
|
|
|
|
## Purpose
|
|
|
|
As projects grow, the need for transferring data between layers (like from a database to a UI) becomes essential.
|
|
However, with that comes the additional complexity of maintaining DTOs and mapping classes. One of the ways this can be
|
|
solved is by using source generators to automate the creation of these classes.
|
|
|
|
Source generators provide the ability to generate code at compile time, which can help reduce boilerplate code and improve maintainability
|
|
and reduce the reliance on expensive reflection-based mapping.
|
|
|
|
## How It Works
|
|
|
|
The project **MapperSourceGen.SourceGenerator** contains a source generator that scans for classes decorated with the `MapperAttribute`.
|
|
When it finds such a class, it generates a DTO and a mapping class for it.
|
|
|
|
### Example
|
|
|
|
Let's say you have a class `Order` that you want to generate a DTO and mapping class for. You would decorate it with the `GenerateMapperAttribute` like this:
|
|
|
|
**Order.cs**
|
|
```csharp
|
|
[Mapper] // tells the source generator to generate a DTO and mapping class for this class
|
|
public sealed class Order
|
|
{
|
|
[MapAlias("EntityId")] // renames Id to EntityId in the DTO
|
|
public Guid Id { get; set; }
|
|
|
|
public Guid? CustomerId { get; set; }
|
|
|
|
[MapAlias("OrderId")] // renames IncrementId to OrderId in the DTO
|
|
public int IncrementId { get; set;}
|
|
|
|
public DateTimeOffset Created { get; set;}
|
|
|
|
public DateTimeOffset? Updated { get; set; }
|
|
|
|
[MapIgnore] // this property will not be included in the DTO or mapper
|
|
public string TransactionId { get; set; }
|
|
}
|
|
```
|
|
|
|
The source generator will then generate a DTO class `OrderDto` and a mapping class `OrderMapper` that looks like this:
|
|
|
|
**OrderMapper.g.cs**
|
|
```csharp
|
|
public sealed partial class OrderMapper
|
|
{
|
|
public static Order ToModel(OrderDto source)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(source);
|
|
|
|
return new Order()
|
|
{
|
|
Id = source.EntityId,
|
|
CustomerId = source.CustomerId,
|
|
IncrementId = source.OrderId
|
|
Created = source.Created,
|
|
Updated = source.Updated
|
|
}
|
|
}
|
|
|
|
public static OrderDto ToDto(Order source)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(source);
|
|
|
|
return new OrderDto()
|
|
{
|
|
EntityId = source.Id,
|
|
CustomerId = source.CustomerId,
|
|
OrderId = source.IncrementId,
|
|
Created = source.Created,
|
|
Updated = source.Updated
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**OrderDto.g.cs**
|
|
```csharp
|
|
public sealed partial class OrderDto
|
|
{
|
|
public Guid? CustomerId { get; set; }
|
|
|
|
public Guid EntityId { get; set; }
|
|
|
|
public int OrderId { get; set; }
|
|
|
|
public DateTimeOffset Created { get; set; }
|
|
|
|
public DateTimeOffset? Updated { get; set; }
|
|
}
|
|
```
|
|
|
|
|
|
|