LEARN BUILD GROW TOGETHER
DEVELOPER LEARNING HUB
Learn. Build. Grow Together.

Practical tutorials, real-world examples and interview preparation.

.NETC#SQL SERVERANGULAR
Home /.NET 10 /Mapster in .NET 10: Complete Developer Learning Guide

Mapster in .NET 10: Complete Developer Learning Guide

November 15, 2026 5 min read , , , , , ,
Mapster in .NET 10: Complete Developer Learning Guide
Skills Tutorials • Developer Learning Guide

Mapster in .NET 10

A practical, beginner-to-advanced guide to object mapping in modern .NET applications — from your first Adapt call to EF Core projection, dependency injection, testing, and code generation.

.NET 10C# 14Mapster 10.0.14Beginner to Advanced

01. Introduction to Object Mapping

Most business applications represent the same information in different shapes. A database entity may contain persistence fields, a domain model may express business rules, and an API response should expose only the data a client needs. Object mapping is the process of copying and transforming values from one object type into another.

Entity / ModelDatabase shapeMapsterMapping rulesAdapt / projectionDTO / JSONAPI contract

A mapper transforms object shape; it does not replace business validation or authorization.

For example, an Employee entity may contain an internal salary band, audit fields, and navigation properties. An EmployeeResponse DTO can expose only an identifier, display name, and department.

Angular UIHTTP request / response ASP.NET CoreApplication service MapsterEntity ↔ DTO mappingAdapt / ProjectToType EF CoreSQL database The API owns validation and authorization; Mapster handles object transformation.

Example architecture: Angular frontend + ASP.NET Core Web API + Mapster + EF Core.

02. What Is Mapster?

Mapster is an open-source .NET object-mapping library. It maps objects using conventions and configurable rules. Its common API is the Adapt extension method, while TypeAdapterConfig controls mapping behavior.

Mapster also supports queryable projection and code-generation workflows. These capabilities are useful, but they solve different problems: runtime object mapping, query projection, and generated source code should be selected intentionally.

Code baseline: Examples in this guide use C# 14 and .NET 10 with ASP.NET Core and EF Core where relevant. The focus is Mapster usage in a modern .NET application, not a tutorial on C# language features.
Version used in this guide: Mapster 10.0.14 (stable), .NET 10, and C# 14. The 10.1.0 prerelease line is not used as the baseline. Check NuGet before publishing in case a newer stable release becomes available.

03. Why DTOs Matter

DTO means Data Transfer Object. It describes data crossing a boundary, such as an HTTP request or response. DTOs help prevent database implementation details from becoming public API contracts.

public sealed class Employee
{
    public int Id { get; set; }
    public string FirstName { get; set; } = "";
    public string LastName { get; set; } = "";
    public decimal Salary { get; set; }
    public bool IsDeleted { get; set; }
}

public sealed record EmployeeResponse(
    int Id,
    string FullName
);

The response intentionally omits Salary and IsDeleted. Never rely on mapping configuration alone as a security boundary; expose only safe DTO members and enforce authorization separately.

04. How Mapster Works

At a high level, Mapster reads the source and destination types, applies conventions and configuration, builds a mapping expression, and uses the resulting mapping function. Mapping configuration should be created once and reused rather than rebuilt for every request.

Source typePropertiesConfigurationConventions + rulesMapping functionExpression / delegateDestinationDTO / modelConfigure once • reuse consistently • test the result

The first mapping may involve expression construction or compilation depending on the path and configuration. Do not assume every mapping call has identical cost; benchmark the actual application scenario.

05. Mapster vs Manual Mapping vs AutoMapper

ApproachStrengthTrade-off
Manual mappingExplicit, easy to step through, no mapping dependencyRepetitive as models grow
MapsterConcise convention mapping, configurable rules, projection and code generation optionsRequires understanding configuration and generated expressions
AutoMapperEstablished profile-based mapping ecosystem and broad adoptionMapping behavior is abstracted behind configuration

Choose based on team familiarity, mapping complexity, projection requirements, licensing and deployment constraints, and measured performance. A mapper is not automatically better than a few clear manual assignments.

06. Create a .NET 10 Project

Install the .NET 10 SDK and create a Web API project:

dotnet new webapi -n MapsterDemo
cd MapsterDemo
dotnet run

Use the SDK and target framework supported by your deployment environment. C# 14 features are available when compiling with the .NET 10 toolchain; the mapping examples below use straightforward syntax so the concepts remain clear.

07. Install Mapster

Install the stable Mapster package:

dotnet add package Mapster --version 10.0.14

Optional packages serve additional scenarios:

  • Mapster.DependencyInjection — inject services into mapping logic through Mapster's service mapper integration.
  • Mapster.Tool — command-line code generation.
  • Mapster.Core — lighter core package for scenarios that do not need the full configuration API.

Install only the packages your application needs. Confirm compatible package versions and target frameworks on NuGet.

08. Your First Mapping

When source and destination properties have matching names and compatible types, Mapster can map them by convention.

using Mapster;

public sealed class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public decimal Price { get; set; }
}

public sealed class ProductResponse
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public decimal Price { get; set; }
}

Product product = new()
{
    Id = 10,
    Name = "Keyboard",
    Price = 2499m
};

ProductResponse response = product.Adapt<ProductResponse>();

Adapt<T>() creates and returns a destination object. This is the most direct way to start using Mapster.

09. Mapping to an Existing Object

Use the overload that accepts a destination instance when you need to populate an existing object:

var response = new ProductResponse();
product.Adapt(response);

This is useful for controlled updates, but be deliberate about which properties may change. Mapping a request DTO directly into a tracked entity can overwrite fields the client should not control.

10. Convention-Based Mapping

Mapster conventionally matches destination members to source members by name and compatible type. For example, Id maps to Id and Name maps to Name. Different names, computed values, and security-sensitive members should be configured explicitly.

Important: Convention mapping is a convenience, not a substitute for reviewing the destination contract. Add tests for fields that must never be exposed or overwritten.

11. Explicit Mapping Configuration

Use TypeAdapterConfig when names differ or mapping behavior needs to be documented:

var config = new TypeAdapterConfig();

config.NewConfig<Product, ProductResponse>()
    .Map(dest => dest.Name, src => src.Name);

ProductResponse dto = product.Adapt<ProductResponse>(config);

NewConfig creates/replaces the configuration for that type pair. ForType is useful when you want to extend an existing type-pair configuration rather than replace it.

12. Profiles and IRegister

Mapster does not use AutoMapper's Profile class as its central organization model. A common Mapster pattern is implementing IRegister and grouping rules by feature:

using Mapster;

public sealed class ProductMappingRegister : IRegister
{
    public void Register(TypeAdapterConfig config)
    {
        config.NewConfig<Product, ProductResponse>()
            .Map(d => d.DisplayName, s => s.Name);
    }
}

// During application startup:
var config = new TypeAdapterConfig();
config.Scan(typeof(ProductMappingRegister).Assembly);

Scanning discovers registration classes in the selected assembly. Keep registrations deterministic and avoid scanning unrelated assemblies unnecessarily.

13. Custom Property Mapping

Use Map for renamed or calculated destination members:

config.NewConfig<Employee, EmployeeResponse>()
    .Map(d => d.FullName,
         s => s.FirstName + " " + s.LastName);

Keep mapping expressions focused on transformation. Business decisions such as whether an employee is allowed to view a field belong in application/domain logic, not hidden inside a mapping expression.

14. Ignoring Properties

Use Ignore to prevent a destination member from being populated:

config.NewConfig<EmployeeUpdateRequest, Employee>()
    .Ignore(d => d.Id)
    .Ignore(d => d.CreatedAt)
    .Ignore(d => d.IsAdmin);

Ignoring fields is helpful, but allow-listing writable fields in a dedicated update DTO is safer than accepting a broad entity-shaped request.

15. Nested Objects and Flattening

Mapster can map nested objects when corresponding mappings and member shapes are compatible. It can also map nested source values into flattened destination members:

public sealed class Order
{
    public int Id { get; set; }
    public Customer Customer { get; set; } = new();
}

public sealed class OrderResponse
{
    public int Id { get; set; }
    public string CustomerName { get; set; } = "";
}

config.NewConfig<Order, OrderResponse>()
    .Map(d => d.CustomerName, s => s.Customer.Name);

Nullability matters: if Customer can be null, define and test the expected behavior rather than assuming nested access is safe.

16. Collections and Child Objects

Mapster supports collection mapping. If element types have a mapping, a collection can be adapted to a collection of destination elements:

List<Product> products = GetProducts();

List<ProductResponse> responses =
    products.Adapt<List<ProductResponse>>();

For large collections, consider allocation cost, pagination, and whether the data should be projected by the database instead of loading every entity first.

17. Reverse Mapping

You can configure both directions, but reverse mapping is not automatically safe for every application. A response DTO may contain read-only or calculated members that should never flow back into an entity.

config.NewConfig<Product, ProductResponse>()
    .TwoWays();

For API writes, prefer a dedicated request type and an explicit mapping that ignores keys, audit fields, ownership, and other server-controlled values. Use bidirectional mapping only when both directions genuinely share the same rules.

18. Null Handling and Default Values

Define null behavior at the boundary. A nullable source member may need a nullable destination, a fallback value, or validation failure depending on the domain rule.

config.NewConfig<Customer, CustomerResponse>()
    .Map(d => d.DisplayName,
         s => s.Name ?? "Unknown");

Mapster also supports destination transforms for cross-cutting transformations such as trimming strings. Apply global transforms cautiously because they affect many mappings.

19. Conditional and Runtime Values

When a value is known only at runtime, Mapster's adapter builder can pass parameters through the mapping context:

config.NewConfig<Order, OrderResponse>()
    .Map(d => d.RequestedBy,
         s => (string)MapContext.Current.Parameters["user"]);

var dto = order.BuildAdapter()
    .AddParameters("user", currentUserName)
    .AdaptToType<OrderResponse>();

Use this pattern only when the value is genuinely mapping context. Avoid using it to conceal authorization or business workflow decisions.

20. Custom Conversion and Mapping Logic

For a conversion that cannot be expressed as a simple member rule, use a custom mapping expression or converter configuration. Keep converters deterministic and side-effect free wherever possible.

config.NewConfig<string, Money>()
    .MapWith(value => Money.Parse(value));

Validate invalid input before mapping or ensure the conversion has a clear error contract. Mapping should not unexpectedly perform database calls or network requests.

21. Mapping to Existing EF Core Entities

When updating a tracked EF Core entity, map onto the existing instance rather than creating a second entity instance and attaching it blindly:

var entity = await db.Products
    .SingleOrDefaultAsync(p => p.Id == id);

if (entity is null)
    return Results.NotFound();

request.Adapt(entity);

await db.SaveChangesAsync();

Configure ignored members such as the primary key, tenant identifier, audit fields, and navigation properties. Load the entity through the correct authorization and tenant scope before applying changes.

22. Queryable Projection with ProjectToType

Adapt after materialization Database rows EF entities Adapt<DTO> DTO objects ProjectToType on IQueryable IQueryable<Entity> Projection expression DTO result

ProjectToType builds a query projection; successful SQL translation depends on the LINQ provider and expression support.

ProjectToType<T>() builds a projection expression for an IQueryable. With a supported LINQ provider such as EF Core, the provider may translate the projection into SQL so the application retrieves only the required columns.

var results = await db.Products
    .Where(p => p.IsActive)
    .ProjectToType<ProductResponse>()
    .ToListAsync();
Provider caveat: Not every .NET method or mapping expression can be translated by EF Core. Keep projection expressions provider-friendly and verify generated SQL and behavior with integration tests against the real provider. Do not confuse query projection with mapping an already materialized object.

23. ASP.NET Core Dependency Injection

For applications that prefer an injectable mapper, install Mapster.DependencyInjection. Register a shared configuration and Mapster's service mapper:

var mapsterConfig = new TypeAdapterConfig();
mapsterConfig.Scan(typeof(Program).Assembly);

builder.Services.AddSingleton(mapsterConfig);
builder.Services.AddScoped<MapsterMapper.IMapper,
    MapsterMapper.ServiceMapper>();

Then inject MapsterMapper.IMapper into the application service and call Map<TDestination>(source). Select a lifetime compatible with any services used by mapping logic. If mappings do not require injected services, static Adapt calls with a shared configuration may be sufficient.

24. Mapster in a Clean Architecture API

A practical feature-oriented structure could look like this:

src/
  Api/
    Endpoints/
  Application/
    Products/
      CreateProduct/
      GetProducts/
      ProductMappingRegister.cs
      DTOs/
  Domain/
    Products/
  Infrastructure/
    Persistence/

Keep DTOs and mapping rules near the application feature that owns the use case. Avoid letting infrastructure entities leak into public API contracts. Controllers or endpoints should orchestrate the request, not contain long mapping expressions.

25. Validation and Configuration Compilation

Mapster configuration supports validation-oriented settings such as requiring destination members to have source mappings and requiring explicit type-pair mappings. These settings help teams make mapping rules stricter.

var config = new TypeAdapterConfig
{
    RequireDestinationMemberSource = true
};

// Register mappings before compiling.
config.Scan(typeof(Program).Assembly);
config.Compile();

Strict settings can expose legitimate computed or intentionally ignored members, so configure those rules explicitly. Run compilation/validation in automated tests or startup according to the application's needs.

26. Unit Testing Mapping Rules

Test mapping behavior as application behavior, especially when DTOs protect sensitive data or when custom rules are involved.

[Fact]
public void Product_maps_to_response()
{
    var product = new Product
    {
        Id = 7,
        Name = "Mouse",
        Price = 899m
    };

    var dto = product.Adapt<ProductResponse>();

    Assert.Equal(7, dto.Id);
    Assert.Equal("Mouse", dto.Name);
    Assert.Equal(899m, dto.Price);
}

Add tests for ignored members, null values, nested data, collection mapping, and EF Core projection. Projection tests should use the actual database provider when translation behavior matters.

27. Code Generation with Mapster.Tool

Mapster's code-generation tooling can generate DTOs, extension methods, or mapper implementations from configured models and interfaces. Generated code can make mapping logic visible to the compiler and debugger.

dotnet new tool-manifest
dotnet tool install Mapster.Tool

Generation commands and options depend on the selected workflow. Follow the current Mapster Tool documentation, commit generated source when appropriate for your build process, and inspect generated output rather than treating it as opaque magic.

Use runtime mapping for straightforward scenarios and evaluate code generation when compile-time visibility, trimming/AOT constraints, or measured performance requirements justify the extra build setup.

28. Performance and Best Practices

  • Reuse a configured TypeAdapterConfig; do not create a new global configuration per request.
  • Prefer clear conventions for simple mappings and explicit rules for important differences.
  • Use ProjectToType when query projection is appropriate and supported by the provider.
  • Use pagination and database filtering before materializing large result sets.
  • Keep mapping expressions deterministic and free from hidden I/O.
  • Benchmark representative data sizes and warm/cold paths before making performance claims.
  • Test security-sensitive DTO boundaries and update mappings.

Mapster is designed for fast mapping, but performance depends on configuration, object shape, query provider, allocations, and workload. Measure rather than relying on a generic benchmark headline.

29. Common Mistakes and Troubleshooting

ProblemWhat to check
Destination member is emptyCheck names, types, custom Map rules, and null source values.
Rule appears to be ignoredCheck whether NewConfig replaced an earlier configuration; use ForType when extending.
EF Core projection failsCheck whether every expression is translatable by the provider.
Update overwrites protected fieldsUse a narrow request DTO and explicitly ignore server-owned members.
Circular reference causes recursionReview object graph design and Mapster reference-preservation / depth options.
Startup mapping errorCompile and validate registered mappings in a test or startup path to locate the failing pair.

30. Practical Mini Project: Product API

Build a small API with a persistence entity, create/update request DTOs, and a response DTO. The objective is to practice mapping boundaries rather than to build a full production system.

  1. Create Product, CreateProductRequest, UpdateProductRequest, and ProductResponse.
  2. Configure entity-to-response mapping, including any calculated display fields.
  3. Configure request-to-entity mapping while ignoring the key and audit fields.
  4. Use EF Core to load the existing product before applying an update.
  5. Use ProjectToType for a paginated read endpoint if the provider can translate the projection.
  6. Write unit tests for mapping rules and integration tests for database projection.
public sealed class ProductMappingRegister : IRegister
{
    public void Register(TypeAdapterConfig config)
    {
        config.NewConfig<Product, ProductResponse>();

        config.NewConfig<CreateProductRequest, Product>()
            .Ignore(d => d.Id)
            .Ignore(d => d.CreatedAt);

        config.NewConfig<UpdateProductRequest, Product>()
            .Ignore(d => d.Id)
            .Ignore(d => d.CreatedAt);
    }
}

Complete the project by reviewing which fields clients can read and write. Mapping rules should reinforce that contract, not define it on their own.

31. Interview Questions and Answers

1. What is Mapster?

Mapster is a .NET object-mapping library that maps source objects to destination objects using conventions and configurable rules.

2. What does Adapt<T> do?

It maps a source object to a new destination object of the requested type.

3. What is TypeAdapterConfig?

It stores mapping rules and options for source/destination type pairs and global mapping behavior.

4. What is the difference between NewConfig and ForType?

NewConfig creates/replaces a type-pair configuration; ForType retrieves or creates configuration while preserving and extending an existing one.

5. What is IRegister used for?

It provides a way to organize mapping registrations in classes and discover them through assembly scanning.

6. What is ProjectToType?

It builds a query projection from an IQueryable to a destination type, allowing a compatible provider to translate the projection.

7. When should mapping be manual?

When mapping is small, highly specific, security-sensitive, or clearer as explicit assignments.

8. Why should reverse mapping be used carefully?

Response models can contain calculated or server-controlled members that should not be accepted as input.

9. How do you map to an existing entity?

Load the tracked entity, validate the request and authorization, then map onto the existing instance while ignoring protected members.

10. How do you test Mapster configuration?

Write tests for type-pair rules, important fields, null handling, nested objects, and provider-backed projections.

32. Official Documentation and Further Learning

Use the official documentation and package release notes to verify API details when upgrading. Package APIs and recommended setup can evolve between major versions.

33. Final Thought

Mapster can reduce repetitive mapping code and provide a consistent way to translate between application models. The most important skill is not memorizing extension methods; it is designing clear DTO boundaries, configuring mappings deliberately, and testing the behavior that matters.

Start with convention-based mapping, introduce explicit configuration when the model shapes differ, and use query projection or code generation only when they solve a real requirement. Keep security, validation, and business rules in their proper application layers.

Skills Tutorials – Learn • Build • Grow Together