Regira-Packages

Entity Mapping

EntityMapper

AfterMapper

// interface
public interface IEntityAfterMapper
{
    bool CanMap(object source);
    void AfterMap(object source, object target);
}
public interface IEntityAfterMapper<in TSource, in TTarget> : IEntityAfterMapper
{
    void AfterMap(TSource source, TTarget target);
}
// base class
public abstract class EntityAfterMapperBase<TSource, TTarget> : IEntityAfterMapper<TSource, TTarget>
{
    public abstract void AfterMap(TSource source, TTarget target);
    public bool CanMap(object source)
}

Convention-based mapping (Mapster)

When using Mapster (options.UseMapsterMapping()), the explicit UseMapping<>() and AddMapping<>() statements can be omitted. Mapster maps entities to and from their DTOs by convention, so as long as the entity and its DTOs share a similar structure (matching property names and types), the DTO type arguments on the controller are enough — no per-entity mapping registration is required.

You only need the explicit statements when:

AutoMapper (options.UseAutoMapper()) does not map by convention: every entity ↔ DTO pair must be registered explicitly (each UseMapping<>() / AddMapping<>() call performs a CreateMap). Without it, AutoMapper throws a missing-type-map error.

Configuring the mapping engine

Both UseMapsterMapping(...) and UseAutoMapper(...) accept an optional callback to configure the underlying engine globally:

// Mapster — receives the shared TypeAdapterConfig
options.UseMapsterMapping(config =>
{
    config.Default.IgnoreNullValues(true);
    config.NewConfig<Order, OrderDto>()
        .Map(dto => dto.Total, order => order.Lines.Sum(l => l.Price));
});

// AutoMapper — receives the IServiceProvider and the IMapperConfigurationExpression
options.UseAutoMapper((sp, cfg) =>
{
    cfg.AddProfile<OrderMappingProfile>();
    cfg.CreateMap<Order, OrderDto>()
        .ForMember(dto => dto.Total, opt => opt.MapFrom(o => o.Lines.Sum(l => l.Price)));
});

Defaults applied by Regira (don’t re-set these unless you mean to):

Multiple UseEntities<TContext>() stacks each calling UseMapsterMapping() share one TypeAdapterConfig, so every context’s entity ↔ DTO mappings apply regardless of registration order. The configure delegates run eagerly in call order — the last call wins on conflicting settings.

Dependency Injection

services
    .UseEntities<MyDbContext>(options => {
        // ...

        options.UseMapsterMapping();
        // or
        options.UseAutoMapper();

        // global AfterMapper — register a class implementing IEntityAfterMapper<TSource, TTarget>
        options.AddAfterMapper<MyAfterMapper>();
        // ...or inline, without a dedicated class:
        options.AfterMap<MyModel, MyDto>((source, dto) => { /*...*/ });
    })
    .For<Order>(e =>
    {
        // ...

        // With Mapster this UseMapping<>() is only needed for the After/AfterInput hooks;
        // the Order <-> OrderDto/OrderInputDto mapping itself works by convention.
        e.UseMapping<OrderDto, OrderInputDto>()
            .After((item, dto) => { /*...*/ })
            .AfterInput((dto, item) => { /*...*/ });
        
        // extra mapping config (only required when convention isn't enough, or for AutoMapper)
        e.AddMapping<OrderItem, OrderItemDto>();
        e.AddMapping<OrderItemInputDto, OrderItem>();
    });

Overview

  1. Index — Overview of Regira Entities
  2. Entity Models — Creating and structuring entity models
  3. Services — Implementing entity services and repositories
  4. Mapping — Mapping Entities to and from DTOs
  5. Web Endpoints — Exposing entity operations as HTTP endpoints
  6. Normalizing — Data normalization techniques
  7. Attachments — Managing file attachments
  8. Built-in Features — Ready to use components
  9. Checklist — Step-by-step guide for common tasks
  10. Practical Examples — Complete implementation examples