To get a DI framework to inject dependencies into an Automapper custom TypeConverter, you normally use the ConstructServicesUsing
method of the MapperConfiguration
object. So with ASP.NET Core DI, I expect to be able to configure AutoMapper like this:
public static IMapperConfiguration Configure(IServiceProvider provider)
{
var config = new MapperConfiguration(cfg => {
cfg.AddProfile<MyProfile>();
cfg.ConstructServicesUsing(type => provider.GetService(type));
});
config.AssertConfigurationIsValid();
return config;
}
The MapperConfiguration
object would be configured as an injectable service in startup.cs thus:
public void ConfigureServices(IServiceCollection services)
{
//other service configuration omitted for simplicity
//Automapper config
var provider = services.BuildServiceProvider();
var config = AutoMapperConfig.Configure(provider);
services.AddInstance(config);
}
And the dependency (in this case Automapper itself) would be injected into the TypeConverter constructor like this.
public class MyConverter : ITypeConverter<ThisType, ThatType>
{
private IMapper _mapper;
public MyConverter(IMapperConfiguration mapperConfig)
{
var mc = mapperConfig as MapperConfiguration;
_mapper = mc.CreateMapper();
}
public ThatType Convert(ResolutionContext context)
{
//do something with _mapper here
}
}
I have used this pattern successfully with several DI frameworks, but I can't get it to work with ASP.NET Core. At a guess, I think it might be that Automapper needs to be given the real IServiceProvider instance that is built by .NET after the ConfigureServices method has completed. However, even when I postpone that part of the configuration until the Configure method (see below), the dependency still doesn't get injected into the TypeConverter.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider provider)
{
var config = provider.GetService<IMapperConfiguration>();
config.ConstructServicesUsing(type => provider.GetService(type));
}
So my question is: how do I configure Automapper with ASP.NET Core so that it injects dependencies into custom TypeConverters?
I found the solution to this problem lies in the correct configuration of the ConstructServicesUsing
factory method. As @Tseng pointed out, using the IServiceCollection.AddSingleton
method allows Automapper to be configured in the ConfigureServices
method of Startup.cs, which is where it should be done:
public void ConfigureServices(IServiceCollection services)
{
//other service configuration omitted for simplicity
//Automapper config
services.AddSingleton(provider => AutoMapperConfig.Configure(provider));
}
But crucially, Automapper must be configured to use .NET Core's ActivatorUtilities
class to create service instances (credit to this article for giving me the idea):
public static IMapperConfiguration Configure(IServiceProvider provider)
{
var config = new MapperConfiguration(cfg => {
cfg.AddProfile<MyProfile>();
cfg.ConstructServicesUsing(type => ActivatorUtilities.CreateInstance(provider, type));
});
config.AssertConfigurationIsValid();
return config;
}
With this approach, Automapper is configured to inject any service dependencies into custom TypeConverters
and ValueResolvers
. Just make sure that any such services are also added to the IServiceCollection
instance in ConfigureServices
.
You can/should use a factory method to inject/instantiate the service.
services.AddSingleton<IMapperConfiguration>( (serviceProvider) => AutoMapperConfig.Configure(serviceProvider) );
This way you delay the call to Configure
until the first time IMapperConfiguration
is being resolved. Since it's singleton, it will stay alive for the rest of your application container and further calls to RequestService
will return the same instance.
Edit:
Try removing the config.AssertConfigurationIsValid();
from your AutoMapperConfig.Configure
method and add the following code in Startup.cs
public void Configure(IServiceProvider app)
{
app.VerifyAutoMapperConfig();
}
AutoMapperConfigExtensions.cs
// So one doesn't have to add namespaces to become available
// inside Startup.cs; Recommended way all middleware register their
// AddXxx and UseXxx extension methods
namespace Microsoft.Extensions.DependencyInjection {
public static class AutoMapperConfigExtensions
{
public static void VerifyAutoMapperConfig(this IServiceProvider app)
{
var config = app.RequestService<IMapperConfiguration>();
config.AssertConfigurationIsValid();
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With