【问题标题】:Autofac and AutomapperAutofac 和 Automapper
【发布时间】:2018-10-20 05:09:30
【问题描述】:

我对 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 来创建对象。现在我能看到的唯一方法是做这样的事情:

创建地图() .ConstructUsing(src => 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 尝试创建一个实例时,事情就变得混乱了。

【问题讨论】:

    标签: automapper autofac


    【解决方案1】:

    the docsthe source 看来,您可以将一个函数传递给Mapper 构造函数,该构造函数将通过该函数运行所有服务位置。

    public Mapper(IConfigurationProvider configurationProvider, Func&lt;Type, object&gt; serviceCtor)

    This blog article 有更详细的示例,并解释了如何将 ASP.NET Core DI 与 AutoMapper 连接起来。不要拘泥于它是 ASP.NET Core - 您将遵循 Autofac 的相同原则。

    builder.Register(
      ctx =>
      {
        var scope = ctx.Resolve<ILifetimeScope>();
        return new Mapper(
          ctx.Resolve<IConfigurationProvider>(),
          scope.Resolve);
      })
      .As<IMapper>()
      .InstancePerLifetimeScope();
    

    您的工作只是弄清楚如何将您的配置注册为IConfigurationProvider

    【讨论】:

    • 谢谢!我没有看到使用的 Mapper 构造函数的那个​​版本。但是,当我实施您的建议时,我从 Automap 收到一个错误,即我正在映射的类型的构造函数必须具有零参数,或者只有可选参数。不幸的是,我定义的构造函数必须有参数......并且所有参数都在 Autofac 中注册,所以这应该不是问题(Autofac 能够在其他地方创建对象的其他实例——它只是 Automapper那是有问题的)。就好像 Automapper 没有使用 Autofac 来创建实例一样。
    • 请参阅我发布的答案以了解重要的附加步骤。
    【解决方案2】:

    我将此作为答案发布,但不是>>the

    但是有一个重要但不明显(无论如何对我来说)的额外步骤,您需要实现 Automapper 以使用 Autofac 创建实例:当您定义地图时,您必须明确声明您希望使用构造的对象服务定位器。示例:

    CreateMap<CommunityUserModel, CommunityUserModel>()
        .ConstructUsingServiceLocator()
        .AfterMap( ( src, dest ) => dest.ClearChanged() );
    

    如果您忽略第二行,Automapper 将使用其默认方法来查找无参数或仅可选参数的构造函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-04-02
      • 2019-12-22
      • 1970-01-01
      • 1970-01-01
      • 2019-11-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多