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

Обновлен до AutoMapper 4.2.0, и следуйте руководству по миграции, доступному здесь: https://github.com/AutoMapper/AutoMapper/wiki/Migrating-from-static-API/f4784dac61b91a0df130e252c91a0efd76ff51de. Попытка перевести код на этой странице для 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));
    }
}

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

?

3 ответа

Решение

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

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

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

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));
    }
}

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

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

Для отображения нескольких 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();
    }
}

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

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

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

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

    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 с текущим Профиль в качестве общего аргумента, и, наконец, я регистрирую свой закрытый экземпляр как синглтон. Таким образом, вы можете создавать новые профили без необходимости вручную регистрировать новые оболочки.

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

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

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

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

Другие вопросы по тегам