代码之家  ›  专栏  ›  技术社区  ›  Josh Anderson

使用AutoMapper取消对DTO的格式化

  •  38
  • Josh Anderson  · 技术社区  · 15 年前

    我一直在尝试使用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到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 回复  |  直到 13 年前
        1
  •  8
  •   Omu    8 年前

    使用 https://github.com/omuleanu/ValueInjecter ,是的 asp.net mvc示例应用程序下载 演示所有特性的地方(也包括单元测试)

        2
  •  67
  •   sydneyos    14 年前

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

    基本上,创建从dto到这两个对象的映射,然后将其用作子对象的源。

        3
  •  9
  •   Community CDub    8 年前

    无法发布评论,因此请发布答案。我猜AutoMapper实现中有一些变化,所以请回答 https://stackoverflow.com/a/5154321/2164198 汉索斯提出的建议已不可编辑。尽管有另一种方法可以在这种情况下使用- 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 }; }))
    
        4
  •  9
  •   sanjuro    8 年前

    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; }
    }
    

    自动映射

    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)));
    

    Mapper.Initialize(cfg => cfg.RecognizePrefixes(nameof(Person.Address)));
    
        5
  •  5
  •   Romano Zumbé Ajay Jain    8 年前

    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 }; }))
    
        6
  •  2
  •   Andrei    7 年前

    我有另一个解决办法。主要思想是 自动制版机 know 在展平对象中正确命名特性时:添加嵌套对象特性名称作为前缀。为了你的案子 地址 是前缀:

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

    所以创建熟悉的从嵌套到扁平的映射 然后使用ReverseMap方法可以让AutomMapper了解如何取消嵌套对象的平面化

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

    这就是全部!

        7
  •  1
  •   andres.chort    8 年前

    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; }
    }
    

    可能需要很多改进,但它是有效的。。。