如何使用automapper将简单的poco映射到复杂的对象层次?

时间:2018-03-07 12:45:48

标签: c# automapper

我有一个简单的poco,需要映射到使用复杂对象层次结构的第三方提供的对象。我想尽可能使用AutoMapper,但我不确定如何正确设置它。

我在下面提供了一个简化示例来说明我要做的事情。

我的poco:

public class Person
{
    public string FirstName { get; set; }
    public string Lastname { get; set; }
    public string FullName { get { return Firstname + " " + Lastname; } }
    public string AddressLine1 { get; set; }
    public string AddressLine2 { get; set; }
    public string City { get; set; }
    public string Country { get; set; }
    public string Postcode { get; set; }
    public string Reference { get; set; }
}

第三方对象

public class People
{
    public Person[] Person { get; set; }
}

public class Person
{
    public Names Names { get; set; }
    public Address Address { get; set; }
    public ReasonsForRequest[] Reasons { get; set; }
}

public class Names
{
    public string Fullname { get; set; }
}

public class Address
{
    public string AddressLine[] { get; set; }
    public string City { get; set; }
    public string Country { get; set; }
    public string Postcode { get; set; }
}

public class ReasonsForRequest
{
    public StructuredReasons StructuredReasons { get; set; }
}

public class StructuredReasons
{
    public Reference Ref { get; set; }
}

public class Reference
{
    public string Ref { get; set; }
}

我遇到的问题是注册映射以获取映射的引用。当我需要映射到内部对象和父对象时,如何将mappig注册到只包含对象的对象?

2 个答案:

答案 0 :(得分:4)

[为了清楚起见,我将打电话给你的人员POCO PersonDTO,因为第三方代码也有一个名为Person的课程。]

有几种方法可以做到这一点。我过去使用过的一种方法,包括设置从PersonDTONames的映射,另一个从PersonDTOAddress,另一个来自PersonDTOReasons。最后,添加从PersonDTOPerson的映射。它看起来像这样(我遗漏了Reasons,但你明白了):

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<PersonDTO, Names>()
        .ForMember(d => d.Fullname, o => o.MapFrom(s => s.FullName));
    cfg.CreateMap<PersonDTO, Address>()            
        .ForMember(d => d.AddressLine,
                o => o.MapFrom(s => new[] { s.AddressLine1, s.AddressLine2 }));
    cfg.CreateMap<PersonDTO, Person>()
        .ForMember(d => d.Names, o => o.MapFrom(s => s))
        .ForMember(d => d.Address, o => o.MapFrom(s => s));
});
var mapper = config.CreateMapper();

var myPerson = new PersonDTO() { 
    FirstName = "Bob", 
    LastName = "Gold",
    AddressLine1 = "123 Main Street", 
    AddressLine2 = "Apt. 2"
};

var theirPerson = mapper.Map<Person>(myPerson);

但最新版本的AutoMapper添加了一个ForPath()方法,通过让您访问内部对象,可以使所有这些变得更简单。现在代码崩溃了:

var config = new MapperConfiguration(cfg => 
    cfg.CreateMap<PersonDTO, Person>()
        .ForPath(d => d.Names.Fullname, o => o.MapFrom(s => s.FullName))
        .ForPath(d => d.Address.AddressLine, 
                o => o.MapFrom(s => new[]  { s.AddressLine1, s.AddressLine2 }))
);
var mapper = config.CreateMapper();

编辑:我遗漏了一个可能会改变这两种方法之间平衡的方面。在第一种方法中,定义了多个映射,您可以免费获得具有匹配名称的任何字段。例如,您无需明确将PesronDTO.City映射到Address.City(如果您将PersonDTO.FullName上的案例更改为PersonDTO.Fullname,您就可以免费获得该ForPath()以及)。但在第二种方法中,即使名称匹配,也必须显式映射所有嵌套字段。在你的情况下,我认为第一种方法可能对你更好,因为你必须只显示3个字段,并免费获得其他4个字段。对于第二种方法,您必须为所有7个字段执行package com.example.hodor.stringcut; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.EditText; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { private EditText textView, textView1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (EditText) findViewById(R.id.editText); textView1 = (EditText) findViewById(R.id.editText1); ArrayList<String> listDate = new ArrayList<>(); ArrayList<String> listDate1 = new ArrayList<>(); listDate.add("10 - 07 - 2017 - 12:32:45"); listDate.add("10 - 07 - 2017 - 12:35:45"); listDate.add("10 - 07 - 2017 - 12:36:45"); listDate.add("10 - 07 - 2017 - 12:38:45"); listDate.add("10 - 07 - 2017 - 12:30:45"); textView.setText(listDate.get(1)); for (int i = 0; i < listDate.size(); i++) { String date = listDate.get(i); listDate1.add(date.substring(0, date.length() -11)); } textView.setText(listDate1.get(1)); } }

答案 1 :(得分:0)

默认情况下,它会尝试将SourceType的属性与DestinationType的属性进行匹配。但是对于您的场景,您将使用ForMember扩展方法添加一些规范,如下所示:

Mapper.CreateMap<Foo, FooDTO>()
.ForMember(e => e.Bars, o => o.ExplicitExpansion());

这里有一个现有的帖子: AutoMapper define mapping level