Как зарегистрировать 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.