Автофак и Автомаппер - PullRequest
0 голосов
/ 10 мая 2018

Меня смущает степень использования Autofac с Automapper для отображения объектов. Я прочитал довольно много материала в Интернете об интеграции двух пакетов, но почти все, кажется, сосредоточены на том, как создавать экземпляры IMapper из профилей Automapper, используя код, похожий на этот, который определяет модуль Autofac (CSContainer. статический экземпляр IContainer от Autofac):

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 пытается создать экземпляр, все идет наперекосяк.

Ответы [ 2 ]

0 голосов
/ 11 мая 2018

Я публикую это как ответ, но не >> ответ <<, потому что хочу отдать должное @TravisIllig, потому что без его участия я бы не смог решить мою проблему. </p>

Но есть важный, но не очевидный (для меня, во всяком случае) дополнительный шаг, который необходимо реализовать, чтобы Automapper использовал Autofac для создания экземпляров: когда вы определяете карту, вы должны явно заявить, что вы хотите, чтобы объект был построен с использованием сервисный локатор. Пример:

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

Если вы пропустите эту вторую строку, Automapper использует свой подход по умолчанию для поиска конструктора без параметров или только с необязательными параметрами.

0 голосов
/ 10 мая 2018

Из документов и источника похоже, что вы можете передать функцию в конструктор Mapper, который будет выполнять всю локацию службы через функцию.

public Mapper(IConfigurationProvider configurationProvider, Func<Type, object> serviceCtor)

Эта статья блога содержит более подробные примеры и объясняет, как подключить 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.

...