Installation

You can install the core package directly from Nuget NeatMapper.

Creating maps

You have to create one or more classes implementing one of the following mapping interfaces:

  • INewMap<TSource, TDestination> to map existing objects to new ones
  • IMergeMap<TSource, TDestination> to map and merge objects with others
  • IProjectionMap<TSource, TDestination> to project objects into new ones, this will create an expression which can be used with LINQ or compiled into a NewMap

You can also create async maps by implementing the interfaces:

  • IAsyncNewMap<TSource, TDestination>
  • IAsyncMergeMap<TSource, TDestination>

If you are on .NET 7 or greater you can use the static versions of the interfaces above instead:

  • INewMapStatic<TSource, TDestination>
  • IMergeMapStatic<TSource, TDestination>
  • IProjectionMapStatic<TSource, TDestination>
  • IAsyncNewMapStatic<TSource, TDestination>
  • IAsyncMergeMapStatic<TSource, TDestination>

If you create a class with more than 1 implementation of the same interface you must implement them explicitly, like below.

public class MyMaps :
    INewMap<Product, ProductDto>,
    IAsyncMergeMap<Category, CategoryDto>,
    IProjectionMap<Book, BookDto>
{
    ProductDto? INewMap<Product, ProductDto>.Map(Product? source, MappingContext context){
        if(source == null)
            return null;
        else{
            return new ProductDto{
                Code = source.Code,
                ...
            };
        }
    }

    async Task<CategoryDto?> IAsyncMergeMap<Category, CategoryDto>.MapAsync(Category? source, CategoryDto? destination, AsyncMappingContext context){
        if(source != null){
            destination ??= new CategoryDto();
            destination.Id = source.Id;
            destination.Parent = await context.Mapper.MapAsync(source.Parent, destination.Parent, context.CancellationToken);
            ...
        }
        return destination;
    }

    Expression<Func<Book, BookDto>> IProjectionMap<Book, BookDto>.Project(ProjectionContext context){
        return source => source == null ? null : new BookDto{ ... };
    }
}

Configuring the services

The easiest way to create a mapper is to use Dependency Injection (DI), which will handle all the configuration for you.

// Add all available maps
services.Configure<CustomMapsOptions>(o => o.TypesToScan = Assembly.GetExecutingAssembly().GetTypes().ToList() );
// Or add specific maps
//services.Configure<CustomMapsOptions>(o => o.TypesToScan = new List<Type>{ typeof(MyMaps), ... });

services.AddNeatMapper();

...

IMapper mapper = serviceProvider.GetRequiredService<IMapper>();
IAsyncMapper asyncMapper = serviceProvider.GetRequiredService<IAsyncMapper>();
IProjector projector = serviceProvider.GetRequiredService<IProjector>();

Mapping objects

Map your objects by invoking the generic (extension) methods available:

  • IMapper
    • mapper.Map<Destination>(source)
    • mapper.Map(source, destination)
  • IAsyncMapper
    • asyncMapper.MapAsync<Destination>(source)
    • asyncMapper.MapAsync(source, destination)
  • IProjector
    • projector.Project<Source, Destination>()
    • IEnumerable extension method enumerable.Project<Destination>(projector)

Note that mapping matches types exactly, so parent or derived classes won’t work.

// Create a new object (source type is auto-inferred)
var myProductDto = mapper.Map<ProductDto>(myProduct);

// Map to an existing object asynchronously (both types are auto-inferred)
await asyncMapper.MapAsync(myCategory, myCategoryDto);

// Create a projection to use in a LINQ query
var myBookDtos = db.Set<Book>()
    .Project<BookDto>(projector)
    .ToArray();