AutoMapper Как сопоставить объект A с объектом B по-разному в зависимости от контекста - PullRequest
10 голосов
/ 29 апреля 2010

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

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

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

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

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

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

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

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

Ответы [ 3 ]

16 голосов
/ 08 апреля 2013

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

Начиная с версии 4.2.1 Automapper имеет санкционированный нестатический преобразователь и конфигурацию (спасибо Джимми!).

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);
5 голосов
/ 29 апреля 2010

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

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

1 голос
/ 29 апреля 2010

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

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

Например:

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.

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...