With hexagonal and onion architectures, clean code, domain-driven-design, command query responsibility segragation Dependency Injection or Inversion of Control comes in very handy.
One thing I came across while working with Autofac and Automapper is, how good the play together.
Imagine a basic setup where we handle an incoming request viewmodel, map it to an input command for a command handler, but the mapping is a little bit more complicated and we already want to use a service from the container during the mapping process. Automapper provides us with ITypeConverter
and IValueResolver
interfaces for more advanced mapping tasks.
For a mapping like this
var target = _mapper.Map<TargetType>(new SourceType());
We could use the following SourceType to TargetType TypeConverter
public class SourceToTargetTypeConverter
: ITypeConverter<SourceType, TargetType>
{
public TargetType Convert(SourceType source, TargetType destination, ResolutionContext context)
{
...
return targetObject;
}
}
It could be quite handy to have access to the Autofac container and request some IFancyService
inside our SourceToTargetTypeConverter
public class SourceToTargetTypeConverter
: ITypeConverter<SourceType, TargetType>
{
private readonly IFancyService _service;
public SourceToTargetTypeConverter(IFancyService service) {
_service = service; // require IFancyService from the container
}
public TargetType Convert(
SourceType source,
TargetType destination,
ResolutionContext context)
{
return new TargetType
{
// use the service
Thing = _service.GetAwesomeThing()
};
}
}
For this we first need to create a MappingProfile
that configures our SourceToTargetTypeConverter
public class FancyProfile : AutoMapper.Profile
{
public FancyProfile()
{
CreateMap<SourceType, TargetType>()
.ConvertUsing<SourceToTargetTypeConverter>();
}
}
Later, when the container gets built we can use the assembly scanning feature of Autofac and register all AutoMapper.Profile
types auomatically.
internal class MapperModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.AssignableTo<Profile>()
.As<Profile>()
.AutoActivate();
...
}
}
Now comes the magic trick: everytime when a mapper is resolved, we need to resolve a MapperConfiguration
first. Because all AutoMapper.Profile
types have been already registered in the container before we are now able to get all AutoMapper.Profile
by resolving context.Resolve<IEnumerable<Profile>>()
- another nice feature of Autofac.
When the actual Mapper is then created using the config.CreateMapper(Func<Type, object> serviceCtor)
overload, Autofac will fulfill all dependencies defined in tha Automapper converters.
// Get all Profiles and add them to the MapperConfiguration
builder.Register(context =>
{
var profiles = context.Resolve<IEnumerable<Profile>>();
var config = new MapperConfiguration(x =>
{
foreach (var profile in profiles)
{
x.AddProfile(profile);
}
});
return config;
}).SingleInstance().AutoActivate().AsSelf();
// Resolve the MapperConfiguration and call CreateMapper()
builder.Register(context =>
{
var ctx = context.Resolve<IComponentContext>();
var config = ctx.Resolve<MapperConfiguration>();
return config.CreateMapper(
t => ctx.Resolve(t) // The magic is happening here: Autofac will now fulfill all dependcies defined in MapperConfigurations
);
});
Works pretty nice that way and it perfectly fits into an hexagonal or onion architecture.
Btw, thanks Mario for pointing me to this idea 😘
Update 2021:
- New automapper version
- Find the updated code in the Github repo