> For the complete documentation index, see [llms.txt](https://dosaic.gitbook.io/dosaic/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://dosaic.gitbook.io/dosaic/plugins/mapping/mapster.md).

# Mapster

`Dosaic.Plugins.Mapping.Mapster` is a Dosaic plugin that wires [Mapster](https://github.com/MapsterMapper/Mapster) into the Dosaic plugin pipeline. It automatically scans all application assemblies at startup, discovers types annotated with `[MapFrom<TSource>]`, and registers the corresponding Mapster mapping rules in `TypeAdapterConfig.GlobalSettings` — no manual mapping registration required.

## Installation

```shell
dotnet add package Dosaic.Plugins.Mapping.Mapster
```

Or add a package reference directly to your `.csproj`:

```xml
<PackageReference Include="Dosaic.Plugins.Mapping.Mapster" Version="" />
```

## Configuration

No configuration is required. `MapsterPlugin` is discovered and activated automatically by the Dosaic source generator (`Dosaic.Hosting.Generator`). At startup it calls `IImplementationResolver.FindAssemblies()` to obtain every assembly in the application and passes them to the internal `MapsterInitializer`, which registers all `[MapFrom<T>]`-based rules into Mapster's global configuration.

There are no `appsettings.yml` / `appsettings.json` keys for this plugin.

## Usage

### Basic property rename

Use `[MapFrom<TSource>(nameof(TSource.Property))]` on the **destination** property to map it from a differently-named property on the source type.

```csharp
public class DbModel
{
    public string Id { get; set; }
    public string LongName { get; set; }
}

public class ModelDto
{
    public string Id { get; set; }

    [MapFrom<DbModel>(nameof(DbModel.LongName))]
    public string Name { get; set; }
}

// Mapping
var dbModel = new DbModel { Id = "1", LongName = "Hello World" };
var dto = dbModel.Adapt<ModelDto>();
// dto.Name == "Hello World"
```

### Nested navigation path

Pass multiple property names to traverse nested objects. Each element in the array is a step in the navigation chain evaluated against the current traversal position.

```csharp
public class Order
{
    public int Id { get; set; }
    public Address ShippingAddress { get; set; }
}

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

public class OrderDto
{
    public int Id { get; set; }

    // Maps from order.ShippingAddress.City
    [MapFrom<Order>(nameof(Order.ShippingAddress), nameof(Address.City))]
    public string City { get; set; }
}

var order = new Order { Id = 42, ShippingAddress = new Address { Street = "Main St", City = "Berlin" } };
var dto = order.Adapt<OrderDto>();
// dto.City == "Berlin"
```

### Collection projection — scalar values

When the navigation path passes through a collection, each element is projected individually. The target property type must be a compatible `IEnumerable<T>`.

```csharp
public class BlogPost
{
    public int Id { get; set; }
    public List<Tag> Tags { get; set; }
}

public class Tag
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class BlogPostDto
{
    public int Id { get; set; }

    // Extracts Tag.Name from every element in BlogPost.Tags
    [MapFrom<BlogPost>(nameof(BlogPost.Tags), nameof(Tag.Name))]
    public IEnumerable<string> TagNames { get; set; }
}

var post = new BlogPost
{
    Id = 1,
    Tags = [new Tag { Id = 10, Name = "dotnet" }, new Tag { Id = 11, Name = "mapster" }]
};
var dto = post.Adapt<BlogPostDto>();
// dto.TagNames == ["dotnet", "mapster"]
```

### Collection projection — object mapping

When the navigation ends at a collection of objects, each element is adapted to the target element type using the globally registered Mapster rules.

```csharp
public class SourceModel
{
    public List<NestedSource> Items { get; set; }
}

public class NestedSource
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class NestedTarget
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class TargetModel
{
    // Each NestedSource element is adapted to NestedTarget
    [MapFrom<SourceModel>(nameof(SourceModel.Items))]
    public IEnumerable<NestedTarget> Items { get; set; }
}

var source = new SourceModel
{
    Items = [new NestedSource { Id = 1, Name = "A" }, new NestedSource { Id = 2, Name = "B" }]
};
var target = source.Adapt<TargetModel>();
// target.Items contains two NestedTarget instances
```

### Null-safe collection mapping

If the source collection is `null`, the mapped destination property is also `null` — no `NullReferenceException` is thrown.

```csharp
public class SourceModel
{
    public List<NestedSource> Items { get; set; } // null
}

public class TargetModel
{
    [MapFrom<SourceModel>(nameof(SourceModel.Items))]
    public IEnumerable<NestedTarget> Items { get; set; }
}

var source = new SourceModel { Items = null };
var target = source.Adapt<TargetModel>();
// target.Items == null  (no exception)
```

### Multiple `[MapFrom]` attributes on a single property

The attribute allows `AllowMultiple = true`, so a single destination property can have multiple source mappings applied (e.g., for different source types).

```csharp
public class TargetModel
{
    [MapFrom<SourceA>(nameof(SourceA.Title))]
    [MapFrom<SourceB>(nameof(SourceB.Headline))]
    public string Name { get; set; }
}
```

### EF Core LINQ projection (`ProjectToType<T>`)

Because the mappings are registered as Mapster expression rules, they are fully translatable to SQL when used with EF Core's `ProjectToType<T>()` extension.

```csharp
// Retrieve only the columns you need — translated to SQL
var dtos = await dbContext.Orders
    .Where(o => o.CustomerId == customerId)
    .ProjectToType<OrderDto>()
    .ToListAsync();
```

## Features

* **Zero configuration** — no YAML/JSON settings required; the plugin activates automatically via the Dosaic source generator.
* **Attribute-driven mapping** — declare all mapping rules inline on destination types using `[MapFrom<TSource>(navigationPath)]`, keeping mapping logic co-located with the DTO.
* **Deep navigation paths** — navigate arbitrarily deep object graphs by providing a sequence of property names; each name is resolved against the current traversal type.
* **Collection projection to scalars** — extract a scalar property from every element of a source collection into an `IEnumerable<TScalar>`.
* **Collection projection to objects** — map a source collection to a destination collection of a different element type, with each element adapted via Mapster.
* **Null-safe collections** — `null` source collections produce `null` destination properties without throwing.
* **EF Core LINQ projection** — registered expression rules are LINQ-translatable, enabling efficient `ProjectToType<T>()` queries that are converted to SQL.
* **Global rule registration** — rules land in `TypeAdapterConfig.GlobalSettings`, making them available everywhere `Adapt<T>()` or `ProjectToType<T>()` is called with no additional setup.
* **Assembly scanning** — leverages `IImplementationResolver.FindAssemblies()` so every assembly in the application is scanned, including plugin assemblies.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://dosaic.gitbook.io/dosaic/plugins/mapping/mapster.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
