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