Подтвердить что ты не робот

Automapper вместе с инъекцией зависимостей

В настоящее время я имею следующее отображение:

Mapper.CreateMap<Journal, JournalDto>();

Теперь Journal содержит член с именем RefTypeID, соответствующее значение которого существует в другой таблице в базе данных; чтобы посмотреть это значение, у меня есть служба, которая обрабатывает простой запрос int -> string. Конфигурация automapper в настоящее время происходит в статическом классе в начале программы. Можно ли переместить код отображения в класс, который вводится в мой контейнер DI или есть лучший способ?

4b9b3361

Ответ 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();
    }

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

Ответ 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 в контроллер вместо репозитория.