Automapper вместе с инъекцией зависимостей
В настоящее время я имею следующее отображение:
Mapper.CreateMap<Journal, JournalDto>();
Теперь Journal
содержит член с именем RefTypeID
, соответствующее значение которого существует в другой таблице в базе данных; чтобы посмотреть это значение, у меня есть служба, которая обрабатывает простой запрос int -> string
. Конфигурация automapper в настоящее время происходит в статическом классе в начале программы. Можно ли переместить код отображения в класс, который вводится в мой контейнер DI или есть лучший способ?
Ответы
Ответ 1
Вот как я это решил:
Я определил интерфейс IMappingCreator
:
public interface IMappingCreator
{
void CreateMappings();
}
Я пошел вперед и реализовал класс с этим интерфейсом (я использую MEF в качестве контейнера DI, откуда берутся атрибуты), который помещается в контейнер DI как IMappingCreator
:
[Export(typeof(IMappingCreator))]
public class Mapping : IMappingCreator
{
private readonly IRefTypesLookup iRefTypesLookup;
[ImportingConstructor]
public Mapping(IRefTypesLookup rtl)
{
iRefTypesLookup = rtl;
}
public void CreateMappings()
{
Mapper.CreateMap<Journal, DisplayJournal>().AfterMap((j, dj) => dj.RefTypeName = iRefTypesLookup.Lookup(j.RefTypeID));
}
}
Наконец, при запуске моего приложения я извлекаю все экземпляры этого интерфейса в контейнере и вызываю метод CreateMappings
на них:
var mappings = container.GetExportedValues<IMappingCreator>();
foreach (IMappingCreator mc in mappings)
{
mc.CreateMappings();
}
Это упрощает первоначальную настройку, так как все создание происходит в одном месте, и вы можете иметь столько создателей картографирования, сколько хотите (однако вы должны свести их к минимуму, возможно, один раз за проект или так, захват все необходимые службы для сопоставления конкретных типов в этом проекте).
Ответ 2
Вы можете зависеть от IMappingEngine
вместо статического класса Mapper
.
Здесь есть хорошая запись в блоге: Извержение AutoMapper с впрыском зависимостей
Ответ 3
Лучше всего использовать распознаватель клиента. Конфигурация сопоставления является статической, поэтому пользовательские преобразователи предназначены для обеспечения отображения для одного элемента:
Mapper.Initialize(cfg => {
cfg.ConstructServicesUsing(type => WhateverMefUsesToGetInstances.GetInstance(type));
cfg.CreateMap<Journal, DisplayJournal>()
.ForMember(dest => dest.RefTypeName,
opt => opt.ResolveUsing<RefTypeNameResolver>());
});
Затем ваш распознаватель становится:
[Export(typeof(IRefTypeNameResolver))]
public class RefTypeNameResolver : ValueResolver<Journal, string>, IRefTypeNameResolver
{
private readonly IRefTypesLookup iRefTypesLookup;
[ImportingConstructor]
public RefTypeNameResolver (IRefTypesLookup rtl)
{
iRefTypesLookup = rtl;
}
protected override string ResolveCore(Journal source)
{
return iRefTypesLookup.Lookup(source.RefTypeID);
}
}
Конфигурация должна выполняться один раз, поэтому API конфигурации предоставляет привязки к API выполнения (преобразователи типов, преобразователи значений и т.д.).
Ответ 4
Вот самый новый способ сделать это...
https://pintoservice.wordpress.com/2016/01/31/dependency-injection-for-automapper-4-2-in-asp-net-vnext-mvc-project/
Хотя я лично добавляю автоматические сопоставления в контроллере, а не в репозитории. Таким образом, вы можете использовать один и тот же репозиторий для разных контроллеров и иметь разные сопоставления. Тем не менее, эта же концепция просто вводит IMapper
в контроллер вместо репозитория.