AutoMapper

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

5.Projection(投影)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章