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

倾然丶 夕夏残阳落幕 提交于 2019-12-06 02:18:19

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.

Richard Slater

NOTE: Travis Illig has provided a hollistic answer to the question which I am marking as the answer as it answers the question in a broad and generic way. However, I also wanted to document the specific solution to my question.

You need to be fairly careful of how you wire up the dependency resolver to AutoMapper, to be precise you must resolve the component context within the closure - failing to do so will result in the context being disposed before AutoMapper ever gets a chance to resolve it's dependencies.

Solution #1

In my example, the following code block that registers the IMapper using the previously defined MapperConfiguration:

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

Can be trivially adapted by using an overload of MapperConfiguration.CreateMapper() that accepts a Func<Type, object> as an argument named serviceCtor that AutoMapper will use to construct dependencies:

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

It's essential that the Component Context context is used as it is declared within the closure, attempting to use c will result in the following exception:

This resolve operation has already ended. When registering components using lambdas, the IComponentContext 'c' parameter to the lambda cannot be stored. Instead, either resolve IComponentContext again from 'c', or resolve a Func<> based factory to create subsequent components from.

Solution #2

Using a very similar technique to Solution #1 it is possible to use the IMapperConfiguration.ConstructServiceUsing(Func<Type, object>) which provides for more readable code. The original code:

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

And the updated code with the call to x.ConstructServiceUsing(constructor):

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

Again if you fail to create an instance of IComponentContext within the closure / lambda the context will have been disposed before the Mapper creates the dependencies.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!