CoreMap is a lightweight, di friendly object mapping library designed for Clean Architecture in modern .NET applications. It promotes manual mapping via handler classes, encouraging full control, maintainability, and testability.
- π DI-integrated mapper resolution β mapping handlers are fully resolved from your DI container
- π§© Supports constructor injection β mapping logic can depend on other services (e.g. complex mapping helper services)
- β»οΈ Scoped mapping handlers by default β efficient and consistent, but configurable
- βοΈ Zero reflection, zero magic β fully explicit, traceable, and easy to debug
- π§ͺ Easy to test β works seamlessly with standard unit test libraries and mocking frameworks
- π Minimal dependencies β compatible with
.NET Standard 2.0and.NET 8.0+
You might ask: Why use CoreMap when powerful libraries like AutoMapper or Mapster already exist?
While those tools are excellent and offer rich features, CoreMap was created with a different goal in mind:
β To enforce manual, explicit mappings that reduce ambiguity and avoid surprises β especially for teams following Clean Architecture and MediatR-style design.
CoreMap favors:
- Clarity over cleverness β no magic, no reflection, no unexpected behavior.
- Mental flow consistency β you're already using
IRequest+IRequestHandlerpatterns with MediatR-Style and with CoreMap, you follow the same structure:
DTOs & Entities+IMapHandler<TInput, TOutput>.
This reduces mental context switching and increases speed through familiarity. - New dev friendliness β explicit handler-based mapping makes the codebase easier to reason about for anyone unfamiliar with AutoMapper-style conventions.
- Strict control β essential for mapping across boundaries like DTOs β domain models, where implicit behavior can introduce subtle bugs.
- Service injection support β unlike static extension methods, CoreMap handlers are resolved from DI, allowing you to inject services (e.g. ID generators, localization providers) when mappings require context.
CoreMap doesn't try to replace general-purpose mappers β it focuses on intentional mapping in architecturally disciplined applications.
- The project is considered Work In Progress.
- Breaking changes can occur at any time without notice.
- No guarantees are made about stability or upgrade paths.
Follows a common-sense semantic versioning pattern:
-
Major (
X.0.0)- Introduces major features or architectural changes
- May include well documented breaking changes
-
Minor (
1.X.0)- Adds new features or enhancements
- May include significant bug fixes
- No breaking changes
-
Patch (
1.0.X)- Hotfixes or urgent bug fixes
- Safe to upgrade
- No breaking changes
dotnet add package CoreMappublic record ArticleEntity
{
public Guid Id { get; init; }
public string Title { get; init; } = default!;
public string Description { get; init; } = default!;
public AuthorEntity WrittenBy { get; init; } = default!;
}
public record AuthorEntity
{
public Guid Id { get; init; }
public string Name { get; init; } = default!;
}internal record ArticleResponse
{
public Guid Id { get; init; }
public string Title { get; init; } = default!;
public string Description { get; init; } = default!;
public AuthorResponse Author { get; init; } = default!;
}
internal class AuthorResponse
{
public Guid Id { get; init; }
public string Title { get; init; } = default!;
public string Description { get; init; } = default!;
}internal class AuthorResponseToEntityMap : ICoreMapHandler<AuthorResponse, AuthorEntity>
{
public AuthorEntity Handler(AuthorResponse data, ICoreMap alsoMap) => new AuthorEntity()
{
Id = data.Id,
Name = data.Title
};
}
internal class ArticleResponseToEntityMap : ICoreMapHandler<ArticleResponse, ArticleEntity>
{
public ArticleEntity Handler(ArticleResponse data, ICoreMap alsoMap) => new ArticleEntity()
{
Description = data.Description,
Id = data.Id,
Title = data.Title,
WrittenBy = alsoMap.MapEach(data.Author).To<AuthorEntity>()
};
}public static IServiceCollection AddServices(IServiceCollection services)
{
// Assembly-scanned registration
services.AddCoreMap(o => { }, new Type[]
{
typeof(Startup)
});
// Or manual registration
services.AddCoreMap(o => { });
services.AddScoped<ICoreMapHandler<ArticleResponse, ArticleEntity>, ArticleResponseToEntityMap>();
services.AddScoped<ICoreMapHandler<AuthorResponse, AuthorEntity>, AuthorResponseToEntityMap>();
return services;
}ArticleEntity item = coreMap.Map(response).To<ArticleEntity>();ICollection<ArticleEntity> entities =
coreMap.MapEach(responses).To<ArticleEntity>();ArticleEntity item = coreMap.MapTo<ArticleResponse, ArticleEntity>(response);ICollection<ArticleEntity> entities =
coreMap.MapEachTo<ArticleResponse, ArticleEntity>(responses);