web-dev-qa-db-ja.com

AutofacとAutomapper

AutofacをAutomapperで使用してオブジェクトをマップできる範囲について混乱しています。 2つのパッケージの統合についてオンラインでかなりの資料を読みましたが、ほとんどすべてが、Autofacモジュールを定義する次のようなコードを使用してAutomapperプロファイルからIMapperのインスタンスを作成する方法に焦点を当てているようです(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 => 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がインスタンスを作成しようとしたときに、問題が発生します。

3
Mark Olbert

私はこれを回答として投稿していますが、>> the <<回答ではありません。@ TravisIlligにクレジットを与えたいので、彼の入力がなければ問題を解決できなかったでしょう。

しかし、AutomapperがAutofacを使用してインスタンスを作成するために実装する必要がある重要ですが明白ではない(とにかく)追加の手順があります:マップを定義するとき、サービスロケーターを使用してオブジェクトを構築することを明示的に指定する必要があります。例:

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

その2行目を省略した場合、Automapperは、パラメーターなしまたはオプションのみのパラメーターコンストラクターを探すというデフォルトのアプローチを使用します。

1
Mark Olbert