Использование AutoMapper для раскрепощения DTO - PullRequest
38 голосов
/ 30 июня 2010

Я пытался использовать AutoMapper, чтобы сэкономить некоторое время, переходя от моих DTO к моим доменным объектам, но у меня возникают проблемы с настройкой карты, чтобы она работала, и я начинаю задаваться вопросом, может ли AutoMapper быть неправильный инструмент для работы.

Рассмотрим этот пример объектов домена (одна сущность и одно значение):

public class Person
{
    public string Name { get; set; }
    public StreetAddress Address { get; set; }
}

public class StreetAddress
{
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
}

Мой DTO (из объекта Linq-to-SQL) выглядит примерно так:

public class PersonDTO
{
    public string Name { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
}

Я бы хотел сделать это в моем хранилище:

return Mapper.Map<PersonDTO, Person>(result);

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

Я пробовал несколько различных конфигураций, но вот несколько:

Mapper.CreateMap<PersonDTO, Person>()
    .ForMember(dest => dest.Address, opt => opt.MapFrom(Mapper.Map<Person, Domain.StreetAddress>));

и

Mapper.CreateMap<Person, Domain.Person>()
    .ForMember(dest => dest.Address.Address1, opt => opt.MapFrom(src => src.Address))
    .ForMember(dest => dest.Address.City, opt => opt.MapFrom(src => src.City))
    .ForMember(dest => dest.Address.State, opt => opt.MapFrom(src => src.State));

Я читал, что выравнивание объектов с помощью AutoMapper - это легко, но откатывание их не просто ... или даже возможно. Может кто-нибудь сказать мне, пытаюсь ли я сделать невозможное, и если нет, то, что я делаю неправильно?

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

Ответы [ 7 ]

64 голосов
/ 05 мая 2011

Мне кажется, это тоже работает:

Mapper.CreateMap<PersonDto, Address>();
Mapper.CreateMap<PersonDto, Person>()
        .ForMember(dest => dest.Address, opt => opt.MapFrom( src => src )));

По сути, создайте отображение из dto в оба объекта, а затем используйте его в качестве источника для дочернего объекта.

9 голосов
/ 14 августа 2014

В дополнение к ответу Сиднея и в соответствии с комментарием Тревора де Коеккока, таким образом возможно двухстороннее отображение

public class Person
{
    public string Name { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
}

public class PersonViewModel
{
    public string Name { get; set; }
    public string AddressStreet { get; set; }
    public string AddressCity { get; set; }
    public string AddressState { get; set; }
}

Отображения Automapper

Mapper.Initialize(cfg => cfg.RecognizePrefixes("Address"));
Mapper.CreateMap<Person, PersonViewModel>();
Mapper.CreateMap<PersonViewModel, Address>();
Mapper.CreateMap<PersonViewModel, Person>()
    .ForMember(dest => dest.Address, opt => opt.MapFrom( src => src )));

Если вы реализуете NameOf класс, вы можете избавиться от префикса магической строки

Mapper.Initialize(cfg => cfg.RecognizePrefixes(Nameof<Person>.Property(x => x.Address)));

РЕДАКТИРОВАТЬ: В C # 6

Mapper.Initialize(cfg => cfg.RecognizePrefixes(nameof(Person.Address)));
9 голосов
/ 06 июня 2013

Невозможно опубликовать комментарий, поэтому отправляю ответ. Я предполагаю, что в реализации AutoMapper произошли некоторые изменения, поэтому ответ https://stackoverflow.com/a/5154321/2164198, предложенный HansoS, больше не компилируется Хотя есть и другой метод, который можно использовать в таких сценариях - ResolveUsing:

Mapper.CreateMap<Person, Domain.Person>()
    .ForMember(dest => dest.Address, opt => opt.ResolveUsing( src => { return new Address() {Address1 = src.Address, City = src.City, State = src.State }; }))
8 голосов
/ 01 июля 2010

используйте https://github.com/omuleanu/ValueInjecter, он делает выравнивание и развертывание , и все, что вам нужно, есть пример приложения asp.net mvc загрузка , где демонстрируются все функции (также модульные тесты)

5 голосов
/ 01 марта 2011

Это может быть поздно, но вы можете решить это, используя лямбда-выражения для создания объекта, подобного этому:

Mapper.CreateMap<Person, Domain.Person>()
        .ForMember(dest => dest.Address, opt => opt.MapFrom( src => { return new Address() {Address1 = src.Address, City = src.City, State = src.State }; }))
1 голос
/ 22 апреля 2017

Я использую это

public static void Unflatten<TSource, TDestination, TMember>(this IMemberConfigurationExpression<TSource, TDestination, TMember> opt)
{
    var prefix = opt.DestinationMember.Name;
    var memberProps = typeof(TMember).GetProperties();
    var props = typeof(TSource).GetProperties().Where(p => p.Name.StartsWith(prefix))
        .Select(sourceProp => new
        {
            SourceProp = sourceProp,
            MemberProp = memberProps.FirstOrDefault(memberProp => prefix + memberProp.Name == sourceProp.Name)
        })
        .Where(x => x.MemberProp != null);
    var parameter = Expression.Parameter(typeof(TSource));

    var bindings = props.Select(prop => Expression.Bind(prop.MemberProp, Expression.Property(parameter, prop.SourceProp)));
    var resolver = Expression.Lambda<Func<TSource, TMember>>(
        Expression.MemberInit(Expression.New(typeof(TMember)), bindings),
        parameter);

    opt.ResolveUsing(resolver.Compile());
}

Конфигурация

new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Person, PersonDTO>();
    cfg.CreateMap<PersonDTO, Person>().ForMember(x => x.HomeAddress, opt => opt.Unflatten());
});

Модели

public class Person
{
    public string Name { get; set; }
    public Address HomeAddress { get; set; }
}

public class Address
{
    public string Line1 { get; set; }
    public string Line2 { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string ZipCode { get; set; }
}

В соответствии с правилами выравнивания AutoMapper

public class PersonDTO
{
    public string Name { get; set; }
    public string HomeAddressLine1 { get; set; }
    public string HomeAddressLine2 { get; set; }
    public string HomeAddressCity { get; set; }
    public string HomeAddressState { get; set; }
    public string HomeAddressZipCode { get; set; }
}

Вероятно, нужно много улучшений, но это работает ...

0 голосов
/ 27 июня 2018

У меня есть другое решение.Основная идея заключается в том, что AutoMapper знает , как сглаживать вложенные объекты , когда вы правильно называете свойства в сглаженном объекте: добавляя имя свойства вложенного объекта в качестве префикса.Для вашего случая Адрес является префиксом:

public class PersonDTO
{
    public string Name { get; set; }
    public string AddressCity { get; set; }
    public string AddressState { get; set; }
    ...
}

Таким образом, создание знакомого отображения из вложенного в плоское и , а затем использование метода ReverseMap позволяет AutomMapper понять, как разблокировать вложенный объект .

CreateMap<Person, PersonDTO>()
   .ReverseMap();

Вот и все!

...