Search code examples
c#inversion-of-controlautomapperautofacautomapper-5

Configuring AutoMapper to fulfil ITypeConverter<,> constructor dependecies with Autofac


My first time working with Autofac to inject AutoMapper's IMapper interface into classes that have an object mapping requirement. I have made some progress, with a little help, getting the various dependencies added to AutoMapper's register using Assembly Scanning:

builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
    .AsClosedTypesOf(typeof(ITypeConverter<,>))
    .AsImplementedInterfaces();
builder.RegisterAssemblyTypes(typeof(AutoMapperExtensions).Assembly)
    .AssignableTo<Profile>().As<Profile>();

builder.Register(context => {
    var profiles = context.Resolve<IEnumerable<Profile>>();
    return new MapperConfiguration(x => {
        foreach (var profile in profiles) x.AddProfile(profile);
    });
}).SingleInstance().AutoActivate().AsSelf();

builder.Register(context => {
    var componentContext = context.Resolve<IComponentContext>();
    var config = componentContext.Resolve<MapperConfiguration>();
    return config.CreateMapper();
}).As<IMapper>();

This works perfectly for an ITypeConverter<,> that doesn't have any injected dependencies:

public class SourceToDestinationTypeConverter : ITypeConverter<SourceModel, DestinationModel> {
    public DestinationModel Convert(SourceModel source, DestinationModel destination, ResolutionContext context) {
        if (source.Items == null) {
            return null;
        }

        return new DestinationModel {
            FirstItem = source.Items.FirstOrDefault(),
            LastItem = source.Items.LastOrDefault()
        };
    }
}

However from the moment I add a dependency, in this contrived example, a validator:

public class SourceToDestinationTypeConverter : ITypeConverter<SourceModel, DestinationModel> {
    private readonly IValidator<SourceModel> _validator;

    public SourceToDestinationTypeConverter(IValidator<SourceModel> validator) {
        _validator = validator;
    }

    public DestinationModel Convert(SourceModel source, DestinationModel destination, ResolutionContext context) {
        if (!_validator.Validate(source)) return null;

        return new DestinationModel {
            FirstItem = source.Items.FirstOrDefault(),
            LastItem = source.Items.LastOrDefault()
        };
    }
}

The following exception is thrown:

Application.TypeConverters.SourceToDestinationTypeConverter needs to have a constructor with 0 args or only optional args

It seems clear to me that AutoMapper needs to be told to use Autofac to fulfil the dependencies. However, I haven't been able to find out how to tell it to do so.

The full solution is available on GitHub if further clarification of the error is required.


Solution

  • I'm guessing you forgot to add the call to ConstructServicesUsing during AutoMapper configuration. Make sure to do this

    Exactly how to integrate Autofac with your app really depends on what kind of app you have (Windows Service? MVC? Web API? Windows Forms? UAP?) and what your expectations around lifetime scope usage are. None of that was included in your question. However, if you search the web for "autofac constructservicesusing" you come up with plenty of examples including several other StackOverflow questions on the same topic.

    Here's a simple example that shows pretty much exactly what you're doing. If you're using an MVC or Web API app and need per-request-scope support, I have a full blog walkthrough on that.

    By way of a note, I'm not sure if the AutoActivate call is really necessary. SingleInstance is thread-safe and it doesn't actually take that long to build an AutoMapper profile lazily. You may want to try without it, especially if AutoMapper itself isn't executing that part until after AutoActivate has run.