Automapper вместе с Dependency Injection - PullRequest
20 голосов
/ 17 ноября 2010

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

Mapper.CreateMap<Journal, JournalDto>();

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

Ответы [ 4 ]

15 голосов
/ 13 декабря 2011

Лучший способ - использовать клиентский распознаватель.Конфигурация сопоставления должна быть статической, поэтому настраиваемые средства распознавания предназначены для обеспечения сопоставления для одного члена:

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 выполнения (преобразователи типов, преобразователи значений и т. д.)

14 голосов
/ 12 декабря 2011

Вы можете взять зависимость от IMappingEngine вместо использования статического класса Mapper.

Здесь есть хороший пост в блоге: Макетирование AutoMapper с внедрением зависимости

9 голосов
/ 18 ноября 2010

Вот как я это решил:

Я определил 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 голосов
/ 21 мая 2016

Вот самый новый способ сделать это ...

https://pintoservice.wordpress.com/2016/01/31/dependency-injection-for-automapper-4-2-in-asp-net-vnext-mvc-project/

Хотя я лично добавляю авто-отображения в контроллере, а не в репозитории. Таким образом, вы можете использовать один и тот же репозиторий для разных контроллеров и иметь разные отображения. Хотя та же концепция, просто введите IMapper в контроллер вместо хранилища.

...