I'm confused about the extent to which Autofac can be used with Automapper to map objects. I've read quite a bit of material online about integrating the two packages, but almost all seem to focus on how to create instances of IMapper from Automapper Profiles, using code something like this, which defines an Autofac Module (CSContainer.Instance is a static instance of Autofac's IContainer):
public class AutoMapperModule : Module
{
private static object ServiceConstructor( Type type )
{
return CSContainer.Instance.Resolve( type );
}
protected override void Load( ContainerBuilder builder )
{
base.Load( builder );
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
builder.RegisterAssemblyTypes( assemblies )
.Where( t => typeof(Profile).IsAssignableFrom( t ) && !t.IsAbstract && t.IsPublic )
.As<Profile>();
builder.Register( c => new MapperConfiguration( cfg =>
{
cfg.ConstructServicesUsing( ServiceConstructor );
foreach( var profile in c.Resolve<IEnumerable<Profile>>() )
{
cfg.AddProfile( profile );
}
} ) )
.AsSelf()
.AutoActivate()
.SingleInstance();
builder.Register( c => c.Resolve<MapperConfiguration>().CreateMapper( c.Resolve ) )
.As<IMapper>()
.SingleInstance();
}
}
(see http://www.protomatter.co.uk/blog/development/2017/02/modular-automapper-registrations-with-autofac/ for an explanation of this approach)
What I'd like to do is have Automapper call Autofac to create objects. Right now the only way I can see to do this is by doing something like this:
CreateMap() .ConstructUsing( src => CSContainer.Instance.Resolve() );
This works, but feels kludgy. It'd be neater if Automapper tried to discover how to resolve instances using Autofac automagically, behind the scenes as it were.
I thought something like this might do the trick (this is a modified version of that first Autofac Module I cited above):
public class AutoMapperModule : Module
{
protected override void Load( ContainerBuilder builder )
{
base.Load( builder );
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
builder.RegisterAssemblyTypes( assemblies )
.Where( t => typeof(Profile).IsAssignableFrom( t ) && !t.IsAbstract && t.IsPublic )
.As<Profile>();
builder.Register( c => new MapperConfiguration( cfg =>
{
cfg.ForAllMaps( ( map, opts ) =>
opts.ConstructUsing( ( x, y ) => CSContainer.Instance.Resolve(map.DestinationType) ) );
foreach( var profile in c.Resolve<IEnumerable<Profile>>() )
{
cfg.AddProfile( profile );
}
} ) )
.AsSelf()
.AutoActivate()
.SingleInstance();
builder.Register( c => c.Resolve<MapperConfiguration>().CreateMapper( c.Resolve ) )
.As<IMapper>()
.SingleInstance();
}
but this resulted in Autofac throwing an exception, complaining about me trying to re-use a builder that had already been defined (apologies, I don't have the exact wording handy).
Is it possible to integrate Automapper and Autofac so that Automapper resolves new instances via Autofac? If so, what's the best way to do it?
Additional Info
So I implemented the suggested answer as follows:
protected override void Load( ContainerBuilder builder )
{
base.Load( builder );
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
builder.RegisterAssemblyTypes( assemblies )
.Where( t => typeof(Profile).IsAssignableFrom( t ) && !t.IsAbstract && t.IsPublic )
.As<Profile>();
builder.Register( c => new MapperConfiguration( cfg =>
{
cfg.ConstructServicesUsing( ServiceConstructor );
foreach( var profile in c.Resolve<IEnumerable<Profile>>() )
{
cfg.AddProfile( profile );
}
} ) )
.AsSelf()
.AutoActivate()
.SingleInstance();
builder.Register( c =>
{
// these are the changed lines
var scope = c.Resolve<ILifetimeScope>();
return new Mapper(c.Resolve<MapperConfiguration>(), scope.Resolve );
} )
.As<IMapper>()
.SingleInstance();
}
But this leads to an Automapper exception, complaining about the object I'm trying to create via a call to Map() must have zero arguments, or only optional arguments. Yet all the object's constructor arguments are also registered with Autofac, and it has no problem creating instances of the objects by itself elsewhere in my code. It's just when Automapper tries to create an instance that things go haywire.