AutoMapper用于两个对象间的映射,通常用于完成DTO(Data Transfer Object)和Model之间的转换。AutoMapper
1.创建
Mapper.Initialize(cfg => {
cfg.CreateMap<Foo, FooDto>();
cfg.CreateMap<Bar, BarDto>();
});
var fooDto = Mapper.Map<FooDto>(foo);
var barDto = Mapper.Map<BarDto>(bar);
//通常一个AppDomain只需要一个MapperConfiguration,并且在应用程序启动时实例化
var config = new MapperConfiguration(cfg => cfg.CreateMap<Order, OrderDto>());
var mapper = config.CreateMapper();
// or
var mapper = new Mapper(config);
OrderDto dto = mapper.Map<OrderDto>(order);
2.Flattening(扁平化)
映射时,将源对象上的property和method与目标对象的property进行匹配。如果目标对象上的property在原对象上没有匹配的property,method或以Get开头的method,AutoMapper会以PascalCase命名约定把目标对象的成员名称分割成单词中间加点,然后进行匹配。
public class Customer
{
public string Name { get; set; }
}
public class Product
{
public decimal Price { get; set; }
public string Name { get; set; }
}
public class OrderItem
{
public Product Product { get; set; }
//数量
public int Quantity { get; set; }
public OrderItem(Product p, int quantity)
{
Product = p;
Quantity = quantity;
}
public decimal GetTotal()
{
return Quantity * Product.Price;
}
}
public class Order
{
private readonly IList<OrderItem> _orderItems = new List<OrderItem>();
public string OrderName { get; set; }
public Customer Customer { get; set; }
public Order(string name)
{
OrderName = name;
}
public OrderItem[] GetOrderItems()
{
return _orderItems.ToArray();
}
public void AddOrderItem(Product p, int num)
{
_orderItems.Add(new OrderItem(p, num));
}
public decimal GetTotal()
{//订单总额
return _orderItems.Sum(item => item.GetTotal());
}
public int Count()
{//产品总数
return _orderItems.Sum(item => item.Quantity);
}
}
public class OrderDTO
{
//对应Order.OrderName,在Order里面有同名的,直接映射
public string OrderName { get; set; }
//对应Order.Customer.Name,在Order里面找不到,按pascalCase拆分后再映射
public string CustomerName { get; set; }
//对应Order.GetTotal(),没有对于的属性/方法,但有Get开头的对应方法
public decimal Total { get; set; }
//对应Order.Count(),没有对应的属性或以Get开头的方法,但有同名的method
public int Count { get; set; }
//没有对应的属性/方法/以Get开头的方法,结果为null
public string Address { get; set; }
}
//测试代码如下:
//products
var apple = new Product { Name = "apple", Price = 2.0m };
var pear = new Product { Name = "pear", Price = 10m };
//customer
var customer = new Customer() { Name = "Micheal" };
//order
var order = new Order("myOrder") { Customer = customer };
order.AddOrderItem(apple, 15);
order.AddOrderItem(pear, 5);
//config map
AutoMapper.Mapper.Initialize(x => x.CreateMap<Order, OrderDTO>());
//execute map
var dto = AutoMapper.Mapper.Map<OrderDTO>(order);
//var dto1 = AutoMapper.Mapper.Map<Order, OrderDTO>(order);
//
System.Diagnostics.Trace.WriteLine(dto.OrderName + ", " + dto.CustomerName +
", total: " + dto.Total + ", count: " + dto.Count);
//执行结果:
myOrder, Micheal, total: 80.0, count: 20
3.Reverse Mapping and Unflattening
反向映射通过ReverseMap来实现,而反向平坦化也只能通过ReverseMap来实现。Unflattening is only configured for ReverseMap. If you want unflattening, you must configure Entity -> Dto then call ReverseMap to create an unflattening type map configuration from the Dto -> Entity.
public class Customer
{
public string Name { get; set; }
}
public class Order
{
public decimal Total { get; set; }
public Customer Customer { get; set; }
}
public class OrderDto
{
public decimal Total { get; set; }
public string CustomerName { get; set; }
}
//测试代码:
//双向映射
Mapper.Initialize(cfg => {
cfg.CreateMap<Order, OrderDto>()//正向
.ReverseMap();//反向
});
var customer = new Customer { Name = "Bob" };
var order = new Order { Customer = customer, Total = 15.8m };
//正向及其反转
//Order -> OrderDto
var orderDto = Mapper.Map<Order, OrderDto>(order);
System.Diagnostics.Trace.WriteLine(orderDto.CustomerName + ", " + orderDto.Total);//Bob, 15.8
//orderDto -> order
orderDto.CustomerName = "Joe";
Mapper.Map(orderDto, order);
System.Diagnostics.Trace.WriteLine(order.Customer.Name + ", " + order.Total);//Joe, 15.8
//反向及其反转
//OrderDto -> Order
var orderDto1 = new OrderDto { CustomerName = "micheal", Total = 88 };
var order1 = Mapper.Map<Order>(orderDto1);
System.Diagnostics.Trace.WriteLine(order1.Customer.Name + ", " + order1.Total);//micheal, 88
//order1 -> orderDto1
order1.Total = 8;
Mapper.Map(order1, orderDto1);
System.Diagnostics.Trace.WriteLine(orderDto1.CustomerName + ", " + orderDto1.Total);//micheal, 8
4.Customizing reverse mapping
//前面的例子看到AutoMapper会自动把Customer.Name从CustomerName反向映射
//如果使用MapFrom自定义映射,AutoMapper也会尝试将其反向映射,只要MapFrom的path是成员访问器。
Mapper.Initialize(cfg => {
cfg.CreateMap<Order, OrderDto>()
.ForMember(d => d.CustomerName, opt => opt.MapFrom(src => src.Customer.Name))
.ReverseMap();
});
//测试代码同上一例,结果完全相同。
如果需要自定义反转映射,那么可以使用ForPath。
For most cases you shouldn’t need this, as the original MapFrom will be reversed for you. Use ForPath when the path to get and set the values are different.
cfg.CreateMap<Order, OrderDto>()
//自定义映射规则,反向转换时会自定按这个反转映射
.ForMember(d => d.CustomerName, opt => opt.MapFrom(src => src.Customer.Name))
.ReverseMap()
//如果要自定义反转规则,那么就要用ForPath来改变,下面是语法上的示例
.ForPath(s => s.Customer.Name, opt => opt.MapFrom(src => src.CustomerName));
If you do not want unflattening behavior, you can remove the call to ReverseMap and create two separate maps. Or, you can use Ignore:
cfg.CreateMap<Order, OrderDto>()
.ForMember(d => d.CustomerName, opt => opt.MapFrom(src => src.Customer.Name))
.ReverseMap()
.ForPath(s => s.Customer.Name, opt => opt.Ignore());