Как зарегистрировать AutoMapper 4.2.0 с помощью простого инжектора

Обновлен до AutoMapper 4.2.0 и следуйте приведенному ниже руководству по миграции: https://github.com/AutoMapper/AutoMapper/wiki/Migrating-from-static-API/f4784dac61b91a0df130e252c91a0efd76ff51de#preserving-static-feel. Попытка перевести код на этой странице для StructureMap в Simple Injector. Может ли кто-нибудь показать мне, как выглядит этот код в Simple Injector?

StructureMap

public class AutoMapperRegistry : Registry
{
    public AutoMapperRegistry()
    {
        var profiles =
            from t in typeof (AutoMapperRegistry).Assembly.GetTypes()
            where typeof (Profile).IsAssignableFrom(t)
            select (Profile)Activator.CreateInstance(t);

        var config = new MapperConfiguration(cfg =>
        {
            foreach (var profile in profiles)
            {
                cfg.AddProfile(profile);
            }
        });

        For<MapperConfiguration>().Use(config);
        For<IMapper>().Use(ctx => ctx.GetInstance<MapperConfiguration>().CreateMapper(ctx.GetInstance));
    }
}

Простой инжектор

?

Ответ 1

Это будет эквивалент:

container.RegisterSingleton<MapperConfiguration>(config);
container.Register<IMapper>(() => config.CreateMapper(container.GetInstance));

Ответ 2

Интерфейс Simple Injector IPackage выглядит как ближайший эквивалент типа StructureMap Registry. Здесь используется пакет, построенный из @Steven answer:

using System;
using System.Linq;
using System.Reflection;
//
using AutoMapper;
//
using SimpleInjector;
using SimpleInjector.Packaging;

public class AutoMapperPackage : IPackage
{
    public void RegisterServices(Container container)
    {
        var profiles = Assembly.GetExecutingAssembly()
            .GetTypes()
            .Where(x => typeof(AutoMapper.Profile).IsAssignableFrom(x));

        var config = new MapperConfiguration(cfg =>
        {
            foreach (var profile in profiles)
            {
                cfg.AddProfile(Activator.CreateInstance(profile) as AutoMapper.Profile);
            }
        });

        container.RegisterSingleton<MapperConfiguration>(config);
        container.Register<IMapper>(() => config.CreateMapper(container.GetInstance));
    }
}

Вам нужно будет добавить пакет SimpleInjector.Packaging, а затем добавить вызов container.RegisterPackages(); в ваш код начальной загрузки/конфигурации.

По сути, единственное, что действительно меняется из StructureMap, это две последние строки.

Ответ 3

Для сопоставления нескольких IMapper с различными объектами MapperConfiguration, которые кажутся несколько повторяющейся проблемой, я рекомендую следующий подход, который даже не требует рефакторинга вызовов метода mapper:

1) Создайте общую оболочку вокруг интерфейса IMapper. Эта оболочка может быть либо интерфейсом, либо классом, но, очевидно, в конечном итоге вам нужно реализовать свою оболочку, поэтому я покажу конкретный класс ниже. Попросите этот обертку реализовать (или наследовать, если вы выбрали интерфейс) интерфейс IMapper, например:

public class ProfileMapper<TProfile> : IMapper where TProfile : Profile
{
    private IMapper mapper;
    private Profile profile;

    public ProfileMapper(TProfile profile)
    {
        this.profile = profile;
        this.mapper = new MapperConfiguration( cfg => cfg.AddProfile( this.profile ) )
                    .CreateMapper();
    }
}

Общий аргумент должен быть подклассом "Профиль", потому что из этого профиля вы получите конфигурацию вашего картографа.

2) В этом классе реализуйте интерфейс IMapper просто перенаправлением вызовов на частный экземпляр IMapper, который вы создаете в конструкторе, например:

public TDestination Map<TDestination>(object source)
{
    return mapper.Map<TDestination>( source );
}

3) Теперь вам нужно зарегистрировать в Simple Injector частично закрытый экземпляр этого класса ProfileMapper для каждого вашего профиля. Вы делаете это сначала, получая все классы, которые наследуют от профиля, затем создавая этот частично закрытый экземпляр, а затем регистрируя его. Существует несколько способов получить все классы профиля, но я пошел с этим:

    IEnumerable<Type> profileRegistrations =
                from type in profileAssembly.GetExportedTypes()
                where type.Namespace == "Namespace.Of.My.Profiles"
                where type.BaseType.Equals( typeof( Profile ) )
                select type;

    foreach (Type profileType in profileRegistrations)
    {
        Container.RegisterSingleton( profileType, profileType );
        Type mapperClosedType = typeof( ProfileMapper<> ).MakeGenericType( profileType );
        Container.RegisterSingleton( typeof( ProfileMapper<> ), mapperClosedType );
    }

Этот код сначала получает все типы, которые наследуются от профиля, расположенного в указанном пространстве имен. Затем для каждого профиля я регистрирую его с помощью SimpleInjector (не обязательно, поскольку они являются конкретными типами и как таковые могут быть созданы контейнером "на лету" ), тогда я делаю частично закрытый экземпляр класса ProfileWrapper с текущим Профиль как общий аргумент, а затем, наконец, я зарегистрирую свой закрытый экземпляр как Singleton. Таким образом, вы можете создавать новые профили без необходимости вручную регистрировать новые Wrappers.

И что это. Теперь вместо того, чтобы вписываться в IMapper и вводить его, вы вводите свой профиль ProfileWrapper профилем, который хотите использовать, например:

ProfileMapper<ApplicationProfile> appProfileMapper;
ProfileMapper<MvcProfile> mvcProfileMapper;
ProfileMapper<GuestProfile> guestProfile;

и т.д. Каждый Wrapper был создан с использованием отдельной MapperConfiguration с использованием разных профилей. Поскольку обертка реализует IMapper, весь ваш код сопоставления остается прежним. Нет необходимости рефакторировать вызовы метода, просто типы зависимостей.

Если вы создаете BaseProfile, просто измените общий параметр в ProfileMapper, чтобы принимать только экземпляры для этого базового файла.