我很担心Autofac可以在多大程度上与Automapper一起使用来映射对象。我已经在网上阅读了很多有关集成这两个软件包的资料,但几乎所有材料似乎都专注于如何使用类似这样的代码来自Automapper Profiles创建IMapper实例,该代码定义了一个Autofac模块(CSContainer.Instance)是Autofac的IContainer的静态实例:
public class AutoMapperModule : Module
{
private static object ServiceConstructor( Type type )
{
return CSContainer.Instance.Resolve( type );
}
protected override void Load( ContainerBuilder builder )
{
base.Load( builder );
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
builder.RegisterAssemblyTypes( assemblies )
.Where( t => typeof(Profile).IsAssignableFrom( t ) && !t.IsAbstract && t.IsPublic )
.As<Profile>();
builder.Register( c => new MapperConfiguration( cfg =>
{
cfg.ConstructServicesUsing( ServiceConstructor );
foreach( var profile in c.Resolve<IEnumerable<Profile>>() )
{
cfg.AddProfile( profile );
}
} ) )
.AsSelf()
.AutoActivate()
.SingleInstance();
builder.Register( c => c.Resolve<MapperConfiguration>().CreateMapper( c.Resolve ) )
.As<IMapper>()
.SingleInstance();
}
}
(有关此方法的说明,请参阅http://www.protomatter.co.uk/blog/development/2017/02/modular-automapper-registrations-with-autofac/)
我想做的是让Automapper调用Autofac来创建对象。现在,我能看到这样做的唯一方法是做这样的事情:
CreateMap() .ConstructUsing(src =&gt; CSContainer.Instance.Resolve());
这很有效,但感觉很糟糕。如果Automapper试图在幕后发现如何使用Autofac自动解析实例,那么它就更整洁了。
我认为这样的事情可能会解决问题(这是我上面引用的第一个Autofac模块的修改版本):
public class AutoMapperModule : Module
{
protected override void Load( ContainerBuilder builder )
{
base.Load( builder );
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
builder.RegisterAssemblyTypes( assemblies )
.Where( t => typeof(Profile).IsAssignableFrom( t ) && !t.IsAbstract && t.IsPublic )
.As<Profile>();
builder.Register( c => new MapperConfiguration( cfg =>
{
cfg.ForAllMaps( ( map, opts ) =>
opts.ConstructUsing( ( x, y ) => CSContainer.Instance.Resolve(map.DestinationType) ) );
foreach( var profile in c.Resolve<IEnumerable<Profile>>() )
{
cfg.AddProfile( profile );
}
} ) )
.AsSelf()
.AutoActivate()
.SingleInstance();
builder.Register( c => c.Resolve<MapperConfiguration>().CreateMapper( c.Resolve ) )
.As<IMapper>()
.SingleInstance();
}
但是这导致Autofac抛出异常,抱怨我试图重新使用已经定义的构建器(道歉,我没有准确的措辞)。
是否可以集成Automapper和Autofac,以便Automapper通过Autofac解析新实例?如果是这样,最好的方法是什么?
其他信息
所以我按如下方式实施了建议的答案:
protected override void Load( ContainerBuilder builder )
{
base.Load( builder );
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
builder.RegisterAssemblyTypes( assemblies )
.Where( t => typeof(Profile).IsAssignableFrom( t ) && !t.IsAbstract && t.IsPublic )
.As<Profile>();
builder.Register( c => new MapperConfiguration( cfg =>
{
cfg.ConstructServicesUsing( ServiceConstructor );
foreach( var profile in c.Resolve<IEnumerable<Profile>>() )
{
cfg.AddProfile( profile );
}
} ) )
.AsSelf()
.AutoActivate()
.SingleInstance();
builder.Register( c =>
{
// these are the changed lines
var scope = c.Resolve<ILifetimeScope>();
return new Mapper(c.Resolve<MapperConfiguration>(), scope.Resolve );
} )
.As<IMapper>()
.SingleInstance();
}
但是这导致了一个Automapper异常,抱怨我试图通过调用Map()创建的对象必须有零参数,或者只有可选参数。然而,所有对象的构造函数参数也在Autofac中注册,并且在我的代码中其他地方创建对象的实例也没有问题。只有当Automapper尝试创建一个事情变得乱七八糟的时候。
答案 0 :(得分:3)
从the docs和the source看起来您可以将函数传递给将通过该函数运行所有服务位置的Mapper
构造函数。
public Mapper(IConfigurationProvider configurationProvider, Func<Type, object> serviceCtor)
This blog article有更详细的示例,并解释了如何使用AutoMapper连接ASP.NET Core DI。不要挂起它作为ASP.NET核心 - 你将遵循Autofac的相同原则。
builder.Register(
ctx =>
{
var scope = ctx.Resolve<ILifetimeScope>();
return new Mapper(
ctx.Resolve<IConfigurationProvider>(),
scope.Resolve);
})
.As<IMapper>()
.InstancePerLifetimeScope();
您的工作就是弄清楚如何将配置注册为IConfigurationProvider
。
答案 1 :(得分:0)
我将此作为答案发布,但不是&gt;&gt;&lt;&lt;回答,因为我想赞扬@TravisIllig,因为没有他的意见,我就无法解决我的问题。
但是,对于Automapper来说,使用Autofac创建实例需要实现一个重要但不明显(对我而言)的额外步骤:当您定义地图时,您必须明确说明您想要的使用服务定位器构造的对象。例如:
CreateMap<CommunityUserModel, CommunityUserModel>()
.ConstructUsingServiceLocator()
.AfterMap( ( src, dest ) => dest.ClearChanged() );
如果您将第二行留下,Automapper将使用其默认方法来查找无参数或仅可选参数构造函数。