我第一次使用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。
答案 0 :(得分:3)
注意: Travis Illig提供了一个hollistic answer to the question,我将其标记为答案,因为它以广泛和通用的方式回答了问题。但是,我还想在我的问题中记录特定解决方案。
您需要非常小心如何将依赖项解析器连接到AutoMapper,准确地说,您必须解析闭包内的组件上下文 - 如果不这样做将导致在AutoMapper获得机会之前处理上下文解决它的依赖关系。
在我的示例中,以下代码块使用先前定义的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<>
的工厂以从中创建后续组件。
使用与解决方案#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本身没有执行该部分。