Seamless AutoMapper integration for SimpleInjector with automatic registration and configuration
This library eliminates the boilerplate of manually registering AutoMapper with SimpleInjector. It automatically:
- 🔍 Scans assemblies for
Profileclasses and registers them - 🔄 Auto-registers all AutoMapper extensions (
ITypeConverter,IValueResolver,IMemberValueResolver,IValueConverter,IMappingAction) - 💉 Handles dependency injection for your custom converters and resolvers
- ⚙️ Configures everything with a single fluent API call
- 🎯 Supports .NET 8, 9, and 10
dotnet add package AdaskoTheBeAsT.AutoMapper.SimpleInjectorpublic class UserProfile : Profile
{
public UserProfile()
{
CreateMap<UserEntity, UserDto>();
CreateMap<UserDto, UserEntity>();
}
}using AdaskoTheBeAsT.AutoMapper.SimpleInjector;
using SimpleInjector;
var container = new Container();
// Option 1: Scan by marker type (recommended)
container.AddAutoMapper(typeof(UserProfile));
// Option 2: Scan multiple assemblies
container.AddAutoMapper(typeof(UserProfile).Assembly, typeof(OrderProfile).Assembly);
// Option 3: Scan with configuration
container.AddAutoMapper(cfg =>
{
cfg.WithMapperAssemblyMarkerTypes(typeof(UserProfile));
cfg.AsScoped(); // Change lifestyle if needed
});public class UserService
{
private readonly IMapper _mapper;
public UserService(IMapper mapper)
{
_mapper = mapper;
}
public UserDto GetUser(UserEntity entity)
{
return _mapper.Map<UserDto>(entity);
}
}This library automatically registers the following with your container:
| Type | Registration | Description |
|---|---|---|
IConfigurationProvider |
Singleton | AutoMapper configuration |
IMapper |
Singleton* | Main mapper instance |
ITypeConverter<,> |
Transient | Custom type converters |
IValueConverter<,> |
Transient | Custom value converters |
IValueResolver<,,> |
Transient | Custom value resolvers |
IMemberValueResolver<,,,> |
Transient | Custom member resolvers |
IMappingAction<,> |
Transient | Custom mapping actions |
*Lifestyle is configurable (Singleton/Scoped/Transient)
container.AddAutoMapper(typeof(UserProfile), typeof(OrderProfile));public static class AutoMapperConfigurator
{
private const string NamespacePrefix = "YourCompany.YourProject";
public static void Configure(Container container)
{
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
var assemblies = new List<Assembly>();
var mainAssembly = typeof(AutoMapperConfigurator).Assembly;
var refAssemblies = mainAssembly.GetReferencedAssemblies();
foreach (var assemblyName in refAssemblies
.Where(a => a.FullName.StartsWith(NamespacePrefix, StringComparison.OrdinalIgnoreCase)))
{
var assembly = loadedAssemblies.Find(l => l.FullName == assemblyName.FullName)
?? AppDomain.CurrentDomain.Load(assemblyName);
assemblies.Add(assembly);
}
container.AddAutoMapper(assemblies);
}
}Your custom resolvers can have dependencies automatically injected:
public class DependencyResolver : IValueResolver<Source, Dest, int>
{
private readonly ISomeService _service;
public DependencyResolver(ISomeService service)
{
_service = service;
}
public int Resolve(Source source, Dest dest, int destMember, ResolutionContext context)
{
return _service.Modify(destMember);
}
}
// Registration
container.Register<ISomeService, SomeService>();
container.AddAutoMapper(typeof(DependencyResolver));
// The resolver will automatically get ISomeService injected!container.AddAutoMapper(cfg =>
{
cfg.WithMapperAssemblyMarkerTypes(typeof(UserProfile));
cfg.AsScoped(); // Use Scoped lifestyle
// or cfg.AsTransient(); // Use Transient lifestyle
// or cfg.AsSingleton(); // Default: Singleton
});Add custom mappings programmatically:
container.AddAutoMapper(cfg =>
{
cfg.WithAssembliesToScan(assemblies);
cfg.WithMapperConfigurationExpressionAction((container, expression) =>
{
// Add custom mappings
expression.CreateMap<Foo, Bar>().ReverseMap();
// Configure global settings
expression.AllowNullCollections = true;
});
});public class MyCustomMapper : IMapper
{
// Your custom implementation
}
container.AddAutoMapper(cfg =>
{
cfg.Using<MyCustomMapper>();
cfg.WithMapperAssemblyMarkerTypes(typeof(UserProfile));
});Perfect for unit tests using Moq or similar frameworks:
var testMapper = new Mock<IMapper>();
testMapper.Setup(m => m.Map<UserDto>(It.IsAny<UserEntity>()))
.Returns(new UserDto { Id = 1, Name = "Test User" });
container.AddAutoMapper(cfg =>
{
cfg.Using(() => testMapper.Object);
cfg.WithMapperAssemblyMarkerTypes(typeof(UserProfile));
});If you're using AutoMapper's premium features:
container.AddAutoMapper(cfg =>
{
cfg.WithLicenseKey("your-license-key");
cfg.WithMapperAssemblyMarkerTypes(typeof(UserProfile));
});Inject IMapper into your services to perform runtime mapping:
public class EmployeesController
{
private readonly IMapper _mapper;
private readonly IEmployeeRepository _repository;
public EmployeesController(IMapper mapper, IEmployeeRepository repository)
{
_mapper = mapper;
_repository = repository;
}
public EmployeeDto GetEmployee(int id)
{
var employee = _repository.GetById(id);
return _mapper.Map<EmployeeDto>(employee);
}
public IEnumerable<EmployeeDto> GetAllEmployees()
{
var employees = _repository.GetAll();
return _mapper.Map<IEnumerable<EmployeeDto>>(employees);
}
}For efficient database queries, use ProjectTo to project directly to DTOs:
public class OrderService
{
private readonly DbContext _dbContext;
private readonly IConfigurationProvider _configurationProvider;
public OrderService(DbContext dbContext, IConfigurationProvider configurationProvider)
{
_dbContext = dbContext;
_configurationProvider = configurationProvider;
}
// Option 1: Using IConfigurationProvider directly (recommended)
public async Task<List<OrderDto>> GetOrdersAsync()
{
return await _dbContext.Orders
.ProjectTo<OrderDto>(_configurationProvider)
.ToListAsync();
}
}Or inject IMapper instead:
public class OrderService
{
private readonly DbContext _dbContext;
private readonly IMapper _mapper;
public OrderService(DbContext dbContext, IMapper mapper)
{
_dbContext = dbContext;
_mapper = mapper;
}
// Option 2: Using IMapper.ConfigurationProvider
public async Task<List<OrderDto>> GetOrdersAsync()
{
return await _dbContext.Orders
.ProjectTo<OrderDto>(_mapper.ConfigurationProvider)
.ToListAsync();
}
}| Method | Description |
|---|---|
AddAutoMapper(params Type[]) |
Register AutoMapper by scanning assemblies containing the specified types |
AddAutoMapper(params Assembly[]) |
Register AutoMapper by scanning the specified assemblies |
AddAutoMapper(IEnumerable<Assembly>) |
Register AutoMapper by scanning the specified assemblies |
AddAutoMapper(IEnumerable<Type>) |
Register AutoMapper by scanning assemblies containing the specified types |
AddAutoMapper(Action<AutoMapperSimpleInjectorConfiguration>) |
Register AutoMapper with custom configuration |
| Method | Description |
|---|---|
WithMapperAssemblyMarkerTypes(params Type[]) |
Specify types to mark assemblies for scanning |
WithAssembliesToScan(IEnumerable<Assembly>) |
Specify assemblies to scan directly |
Using<TMapper>() |
Use a custom IMapper implementation |
Using(Func<IMapper>) |
Use a factory function to create IMapper (useful for testing) |
AsSingleton() |
Register IMapper as Singleton (default) |
AsScoped() |
Register IMapper as Scoped |
AsTransient() |
Register IMapper as Transient |
WithMapperConfigurationExpressionAction(...) |
Add custom mapper configuration |
WithLicenseKey(string) |
Set AutoMapper license key |
- .NET 8.0, 9.0, or 10.0
- AutoMapper 15.1.0+
- SimpleInjector 5.5.0+
Special thanks to:
- Jimmy Bogard for creating AutoMapper
- Steven van Deursen for creating SimpleInjector
This library is based on AutoMapper.Extensions.Microsoft.DependencyInjection and adapted to work seamlessly with SimpleInjector.
Found this helpful? Give it a ⭐ on GitHub!