Autofac和Automapper

时间:2018-05-10 01:38:21

标签: automapper autofac

我很担心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尝试创建一个事情变得乱七八糟的时候。

2 个答案:

答案 0 :(得分:3)

the docsthe 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将使用其默认方法来查找无参数或仅可选参数构造函数。