Have you ever wanted to register Autofac and AutoMapper circularly? By this I mean to do the inception of these two things, at the same time, in the same application:
- Register AutoMapper's mappings through Autofac's component-registration for dependency-injection
- Use registered components in Autofac for dependency-injection into AutoMapper's mapped objects
This is actually possible! It's done by providing AutoMapper with Autofac's IComponentContext
-object, which is not dependent on if you've registered everything you need in the Autofac component-registrations, before configuring AutoMapper. So you can register more components after registering your AutoMapper-configuration and still access all these in your AutoMapper context when they get resolved later in your code.
We'll start with the code for registering the Autofac-components and follow it with some explaining:
public IContainer RegisterComponents()
{
var builder = new ContainerBuilder();
builder.RegisterType<PreMapperComponent>().As<IPreMapperComponent>();
builder.Register(ConfigureMapper).SingleInstance();
builder.RegisterType<PostMapperComponent>().As<IPostMapperComponent>();
return builder.Build();
}
private IMapper ConfigureMapper(IComponentContext context)
{
var configuration = new MapperConfiguration(config =>
{
var ctx = context.Resolve<IComponentContext>();
config.CreateMap<EntityType, DtoType>()
.ConstructUsing(_ =>
new DtoType(ctx.Resolve<IPreMapperComponent>(), ctx.Resolve<IPostMapperComponent>()));
});
return configuration.CreateMapper();
}
The method RegisterComponents
is included to show a somewhat realistic scenario, and for this case specifically showing that you can register components in Autofac both before and after configuring AutoMapper.
The code var ctx = context.Resolve<IComponentContext>()
might look strange, but is crucial for this solution to work. Why not use the context
-argument directly in the ConfigureMapper
-method? Because it will throw an exception starting with This resolve operation has already ended
, and instructs you to resolve IComponentContext
again, which is what we're doing here.
Running the AutoMapper-code
Then we run the code using AutoMapper and the mapping of types needing dependency-injection of constructor-parameters:
public static void Run(IContainer container)
{
var mapper = container.Resolve<IMapper>();
var source = new EntityType(123, "Test-text", "Test-description");
var dto = mapper.Map<DtoType>(source);
dto.RunComponents();
}
The code dto.RunComponents()
is the one that executes the injected implementations of the interfaces IPreMapperComponent
and IPostMapperComponent
used in the type DtoType
.
You can find all the code-files involved in this example in this Gist.
This can all also be done through the recommended approach of Modules in Autofac and Profiles in AutoMapper.