使用AutoMapper取消DTO

Jos*_*son 39 mapping automapper

我一直在尝试使用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; }
}
Run Code Online (Sandbox Code Playgroud)

我的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; }
}
Run Code Online (Sandbox Code Playgroud)

我希望能够在我的存储库中执行此操作:

return Mapper.Map<PersonDTO, Person>(result);
Run Code Online (Sandbox Code Playgroud)

我已经尝试过各种方式配置AutoMapper,但我不断得到通用的Missing类型映射配置或不支持的映射错误,没有详细信息告诉我我失败的地方.

我尝试了很多不同的配置,但这里有几个:

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

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));
Run Code Online (Sandbox Code Playgroud)

我读过,扁平化与AutoMapper对象很容易,但unflattening他们是不容易的......甚至是不可能的.任何人都可以告诉我,我是否正在尝试做不可能的事情,如果不是我做错了什么?

请注意,我的实际对象有点复杂,所以我可能会遗漏错误的关键信息...如果我正在做的事情看起来正确我可以提供更多信息或开始简化我的对象进行测试.

syd*_*yos 65

这似乎对我有用:

Mapper.CreateMap<PersonDto, Address>();
Mapper.CreateMap<PersonDto, Person>()
        .ForMember(dest => dest.Address, opt => opt.MapFrom( src => src )));
Run Code Online (Sandbox Code Playgroud)

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

  • 这将是一个很好的答案,但在我的DTO中,地址属性被展平:"AddressCity",而在Address对象中它们只是"城市",因此除非我明确映射每个字段,否则映射不起作用.这是你必须做的吗? (12认同)

Iva*_*gin 9

无法发表评论,所以发帖回答.我想AutoMapper实现有一些变化,所以回答来自HansoS提出的/sf/answers/360802501/不再可编译.虽然还有另一种方法可以在这种情况下使用 - 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 }; }))
Run Code Online (Sandbox Code Playgroud)

  • 这有效,但它违背了AutoMapper的整个原则.它不再是基于会议的"自动"地图,它是手动映射.更糟糕的是,它是在不透明的外观背后隐藏的手动映射. (4认同)
  • 来自[Automapper wiki](https://github.com/AutoMapper/AutoMapper/wiki):>目前,AutoMapper面向模型投影场景,将复杂对象模型展平为DTO和其他简单对象,其设计更适合序列化,通信,消息传递,或仅仅是域和应用程序层之间的反腐败层.因此,Automapper并非旨在转换DTO-> Entity.但它有很多功能可以根据需要使用.这取决于你 - Automapper只是一个工具. (2认同)

san*_*uro 9

除了sydneyos的答案,根据Trevor de Koekkoek的评论,这种方式可以实现双向映射

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; }
}
Run Code Online (Sandbox Code Playgroud)

自动映射

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 )));
Run Code Online (Sandbox Code Playgroud)

如果实现NameOf类,则可以删除前缀魔术字符串

Mapper.Initialize(cfg => cfg.RecognizePrefixes(Nameof<Person>.Property(x => x.Address)));
Run Code Online (Sandbox Code Playgroud)

编辑:在C#6

Mapper.Initialize(cfg => cfg.RecognizePrefixes(nameof(Person.Address)));
Run Code Online (Sandbox Code Playgroud)

  • 您现在可以使用C#6`nameof`运算符:https://msdn.microsoft.com/en-us/library/dn986596.aspx (2认同)

Omu*_*Omu 8

使用https://github.com/omuleanu/ValueInjecter,它压扁unflattening,和其他任何你需要的,有一个在下载asp.net的MVC示例应用程序,所有的功能都证明(也单元测试)

  • 我一直在尝试ValueInjecter并遇到了一些你在CodePlex上帮助我的问题.现在它工作得很好. (2认同)

小智 5

这可能会迟到但您可以通过使用lambda表达式来创建对象来解决这个问题:

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 }; }))
Run Code Online (Sandbox Code Playgroud)

  • 这会导致错误:'带有语句主体的lambda表达式无法转换为表达式树' (7认同)