配置AutoMapper以实现ITypeConverter<,>构造函数依赖Autofac

时间:2016-10-27 20:23:00

标签: c# inversion-of-control automapper autofac automapper-5

我第一次使用Autofac将AutoMapper的IMapper接口注入到具有对象映射要求的类中。我已经取得了一些进展,with a little help,使用程序集扫描将各种依赖项添加到AutoMapper的寄存器中:

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>();

这适用于没有任何注入依赖项的ITypeConverter<,>

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()
        };
    }
}

但是从我添加依赖项的那一刻起,在这个人为的例子中,验证器是:

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()
        };
    }
}

抛出以下异常:

  

Application.TypeConverters.SourceToDestinationTypeConverter需要一个0 args的构造函数或者只有可选的args

我觉得需要告诉AutoMapper 使用Autofac 来实现依赖关系。但是,我还没有找到如何告诉它这样做。

如果需要进一步澄清错误,则完整的解决方案为available on GitHub

2 个答案:

答案 0 :(得分:3)

  

注意: Travis Illig提供了一个hollistic answer to the question,我将其标记为答案,因为它以广泛和通用的方式回答了问题。但是,我还想在我的问题中记录特定解决方案

您需要非常小心如何将依赖项解析器连接到AutoMapper,准确地说,您必须解析闭包内的组件上下文 - 如果不这样做将导致在AutoMapper获得机会之前处理上下文解决它的依赖关系。

解决方案#1

在我的示例中,以下代码块使用先前定义的IMapper注册MapperConfiguration

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

可以通过使用MapperConfiguration.CreateMapper()的重载进行简单的调整,该重载接受Func<Type, object>作为名为serviceCtor的参数,AutoMapper将使用它来构造依赖项:

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

必须使用组件上下文context,因为它在闭包中声明,尝试使用c将导致以下异常:

  

此解决操作已经结束。使用lambdas注册组件时,无法存储lambda的IComponentContext'c'参数。而是从“IComponentContext”再次解析c,或者解析基于Func<>的工厂以从中创建后续组件。

解决方案#2

使用与解决方案#1非常相似的技术,可以使用IMapperConfiguration.ConstructServiceUsing(Func<Type, object>)来提供更易读的代码。原始代码:

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

通过调用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();

再次,如果您未能在闭包/ lambda中创建IComponentContext的实例,则在Mapper创建依赖项之前,上下文将被释放。

答案 1 :(得分:2)

我猜你在AutoMapper配置期间忘记了add the call to ConstructServicesUsing。一定要这样做

完全如何将Autofac与您的应用集成,实际上取决于您拥有的应用程序类型(Windows服务?MVC?Web API?Windows Forms?UAP?)以及您对生命周期范围使用的期望是。这些都不包括在你的问题中。但是,如果您在网上搜索“autofac constructservicesusing”,那么您会提供大量示例,其中包括有关同一主题的其他几个StackOverflow问题。

Here's a simple example that shows pretty much exactly what you're doing.如果您正在使用MVC或Web API应用,并且需要每个请求范围的支持,I have a full blog walkthrough on that

作为一个说明,我不确定AutoActivate电话是否真的有必要。 SingleInstance是线程安全的,懒惰地构建AutoMapper配置文件实际上并不需要那么长时间。您可能希望在没有它的情况下尝试,特别是如果在AutoActivate运行之后AutoMapper本身没有执行该部分。