3

我第一次使用 Autofac 将 AutoMapper 的IMapper接口注入到具有对象映射要求的类中。在一些帮助下,我取得了一些进展,使用 Assembly Scanning 将各种依赖项添加到 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 个参数或只有可选参数的构造函数

对我来说很清楚需要告诉 AutoMapper 使用 Autofac来满足依赖关系。但是,我无法找到如何告诉它这样做。

如果需要进一步澄清错误,可以在 GitHub 上找到完整的解决方案。

4

2 回答 2

3

注意: Travis Illig 对这个问题提供了一个全面的答案,我将其标记为答案,因为它以一种广泛而通用的方式回答了这个问题。但是,我也想记录我的问题的具体解决方案

您需要非常小心地将依赖解析器连接到 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()接受 aFunc<Type, object>作为名为serviceCtorAutoMapper 将用于构造依赖项的参数的重载来轻松适应:

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

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

此解析操作已结束。使用 lambda 注册组件时,无法存储 lambda的IComponentContext' ' 参数。c相反,要么从 ' 'IComponentContext再次解析,要么解析基于工厂以从中创建后续组件。cFunc<>

解决方案#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 创建依赖项之前被释放。

于 2016-10-28T21:12:18.290 回答
2

我猜你忘了在 AutoMapper 配置期间添加调用。ConstructServicesUsing确保这样做

究竟如何将 Autofac 与您的应用程序集成实际上取决于您拥有什么样的应用程序(Windows 服务?MVC?Web API?Windows 窗体?UAP?)以及您对生命周期范围使用的期望是什么。您的问题中均未包含这些内容。然而,如果你在网上搜索“ autofac constructservicesusing”,你会找到很多例子,包括关于同一主题的其他几个 StackOverflow 问题。

这是一个简单的例子,它几乎准确地显示了你在做什么。如果您使用的是 MVC 或 Web API 应用程序并且需要每个请求范围的支持,我有一个完整的博客演练

顺便说一句,我不确定这个AutoActivate电话是否真的有必要。SingleInstance是线程安全的,懒惰地构建 AutoMapper 配置文件实际上并不需要那么长时间。您可能想尝试不使用它,特别是如果 AutoMapper 本身直到 afterAutoActivate运行才执行该部分。

于 2016-10-28T13:21:58.320 回答