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

AutoMapper Как сопоставить объект A с объектом B, в зависимости от контекста

Вызов всех гуру AutoMapper!

Я хотел бы иметь возможность сопоставлять объект A с объектом B по-разному в зависимости от контекста во время выполнения. В частности, я хотел бы игнорировать некоторые свойства в одном случае отображения и иметь все свойства, отображаемые в другом случае.

То, что я испытываю, заключается в том, что Mapper.CreateMap может быть успешно вызван в разных случаях сопоставления, однако после вызова CreateMap карта для определенной пары типов устанавливается и впоследствии не изменяется с помощью последующих вызовов CreateMap, которые могут описывают отображение по-разному.

Я нашел сообщение в блоге, которое защищает Mapper.Reset(), чтобы обойти проблему, однако статическая природа класса Mapper означает, что это только вопрос времени, прежде чем произойдет столкновение и сбой.

Есть ли способ сделать это?

Мне кажется, что мне нужно вызвать Mapper.CreateMap один раз на приложение, а позже - вызвать Mapper.Map с подсказками о том, какие свойства должны быть включены/исключены.

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

Каковы мои варианты. Что может быть сделано? Automapper кажется таким многообещающим.

4b9b3361

Ответ 1

Класс Mapper - это всего лишь тонкая оболочка поверх объектов Configuration и MappingEngine. Вы можете создавать отдельные экземпляры объектов Configuration/MappingEngine (по-прежнему используя одиночные точки) и использовать свой контейнер IoC для выбора нужного.

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

Ответ 2

Просто чтобы дополнить Jimmy, ответьте здесь код, необходимый для использования AutoMapper без статического Mapper

Начиная с версии 4.2.1 Automapper имеет санкционированную нестационарную карту и конфигурацию (спасибо Jimmy!).

var config = new MapperConfiguration(cfg => {
    cfg.CreateMap<ClassA, ClassB>();
});

var mapper = config.CreateMapper();

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

(исправить для версии 4.1.1)

// Configuration
AutoMapper.Mappers.MapperRegistry.Reset();
var autoMapperCfg = new AutoMapper.ConfigurationStore(new TypeMapFactory(), AutoMapper.Mappers.MapperRegistry.Mappers);
var mappingEngine = new AutoMapper.MappingEngine(autoMapperCfg);
autoMapperCfg.Seal();

//Usage example
autoMapperCfg.CreateMap<ClassA, ClassB>();

var b = mappingEngine.Map<ClassB>(a);

(правильный для версии 3.2.1)

// Configuration
var platformSpecificRegistry = AutoMapper.Internal.PlatformAdapter.Resolve<IPlatformSpecificMapperRegistry>();
platformSpecificRegistry.Initialize();

var autoMapperCfg = new AutoMapper.ConfigurationStore(new TypeMapFactory(), AutoMapper.Mappers.MapperRegistry.Mappers);
var mappingEngine = new AutoMapper.MappingEngine(autoMapperCfg);

//Usage example
autoMapperCfg.CreateMap<ClassA, ClassB>();

var b = mappingEngine.Map<ClassB>(a);

(правильный для версии 2.2.1)

// Configuration
var autoMapperCfg = new AutoMapper.ConfigurationStore(new AutoMapper.TypeMapFactory(), AutoMapper.Mappers.MapperRegistry.AllMappers());
var mappingEngine = new AutoMapper.MappingEngine(autoMapperCfg);

//Usage example
autoMapperCfg.CreateMap<ClassA, ClassB>();

var b = mappingEngine.Map<ClassB>(a);

Ответ 3

Мне кажется, что лучший дизайн может состоять в том, чтобы иметь несколько целевых классов (возможно, наследуя от общей базы или реализуя общий интерфейс)

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

Например:

public class Source
{
    public string Name {get;set;}
    public BigEntity {get;set;}

    /* other members */
}

public class SourceDTO
{
    public string Name {get;set;}
    public BigEntity {get;set;}
}

public class SourceSummaryDTO
{
    public string Name {get;set;}
}

В качестве альтернативы вы можете сделать это:

public class SourceSummaryDTO : SourceDTO
{
    public string Name {get;set;}
    public BigEntity 
    {
        get{throw new NotSupportedException();}
        set{throw new NotSupportedException();}
    }
}

Таким образом, вы можете передать SourceSummaryDTO, как если бы он был SourceDTO.

Наличие условно заполненных свойств звучит как рецепт проблемы для меня - я бы предпочел, чтобы классы были явными в отношении того, что они содержат, особенно с объектами передачи данных.

Для меня самое лучшее в Automapper - это возможность проверить сопоставления, а затем знать, что каждое свойство в классах назначения будет заполнено.