AutoMapper 是一个基于约定的面向对象的映射器,它的功能常用于将一个 input 对象 转成一个不同类型的 output 对象,input 和 output 对象之间的属性可能相同也可能不相同,这一篇我们来一起研究一下 AutoMapper 的一些高级玩法。

安装 AutoMapper

要想在项目中使用 AutoMapper ,需要通过 nuget 引用 AutoMapper 和 AutoMapper.Extensions.Microsoft.DependencyInjection 包,可以通过 Visual Studio 2019 的 NuGet package manager 可视化界面安装 或者 通过 NuGet package manager 命令行工具输入以下命令:



Install-Package AutoMapper
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection


直接装第二个也行吧?反正依赖第一个

配置 AutoMapper

一旦 AutoMapper 成功安装之后,接下来就可以将它引入到 ServiceCollection 容器中,如下代码所示:



public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

services.AddAutoMapper(typeof(AuthorProfile));//应该先创建AuthorProfile.cs文件
}


使用 profiles 统一管理 mapping 信息

可以使用 profiles 来统一组织你的 mapping 信息,要创建 profile,需要实现 AutoMapper 提供的 Profile 类,然后在你刚才创建的 Profile 子类的构造函数中添加映射信息,下面的代码展示了如何创建一个从 Proifle 继承的 AuthorProfile 类以及相关信息。



public class AuthorProfile : Profile
{
public AuthorProfile()
{
CreateMap<AuthorModel, AuthorDTO>();
}
}


接下来再看 AuthorModel 和 AuthorDTO 两个对象的定义:



public class AuthorModel
{
public int Id
{
get; set;
}
public string FirstName
{
get;set;
}
public string LastName
{
get; set;
}
public string Address
{
get; set;
}
}

public class AuthorDTO
{
public int Id
{
get; set;
}
public string FirstName
{
get; set;
}
public string LastName
{
get; set;
}
public string Address
{
get; set;
}


使用 ReverseMap()

值得注意的是,上面的示例是一种 ​​单向流动​​,这是什么意思呢? 举个例子吧,下面是 ​​单向流动​​ 的一段代码。



AutoMapper.Mapper.CreateMap<AuthorDTO, AuthorModel>();


有了这个 Map,接下来就可以轻松实现 AuthorDTO 到 AuthorModel 的转换,代码如下:



var authorModel = AutoMapper.Mapper.Map<AuthorModel>(author);


假设因为某种原因,你需要将 authorModel 实例反转成 authorDTO,这时你用了如下的代码段。



var author = AutoMapper.Mapper.Map<AuthorDTO>(authorModel);


很遗憾,这种方式注定会抛出异常,这是因为 AutoMapper 并不知道如何实现 authorModel 到 authorDTO 的转换,毕竟你没有定义此种 map 的映射流向,那怎么解决呢? 可以再定义一个 CreateMap 映射哈,其实没必要,简单粗暴的做法就是调用 ReverseMap 即可,实现代码如下:



AutoMapper.Mapper.CreateMap<AuthorDTO, AuthorModel>().ReverseMap();


使用 ForMember() 和 MapFrom()

这一节我们继续使用之前说到的 AuthorModel 和 AuthorDTO 类,下面的代码片段展示了如何将 AuthorModel 转成 AuthorDTO 。



var author = new AuthorModel();           
author.Id = 1;
author.FirstName = "Joydip";
author.LastName = "Kanjilal";
author.Address = "Hyderabad";
var authorDTO = _mapper.Map<AuthorDTO>(author);


现在假设我将 AuthorModel 中的 Address 改成 Address1,如下代码所示:



public class AuthorModel
{
public int Id
{
get; set;
}
public string FirstName
{
get; set;
}
public string LastName
{
get; set;
}
public string Address1
{
get; set;
}
}


然后在 AuthorProfile 中更新一下 mapping 信息,如下代码所示:



public class AuthorProfile : Profile
{
public AuthorProfile()
{
CreateMap<AuthorModel, AuthorDTO>().ForMember(destination => destination.Address, map => map.MapFrom(source => source.Address1));
}
}


使用 NullSubstitute

何为 ​​NullSubstitute​​ 呢?大意就是在映射转换的过程中,将input 为null 的属性映射之后做自定义处理,比如在 ouput 中改成 ​​No Data​​,下面的代码展示了如何去实现。



AutoMapper.Mapper.CreateMap<AuthorModel, AuthorDTO>().ForMember(destination => destination.Address, opt => opt.NullSubstitute("No data"));


mapping 的 AOP 拦截

考虑下面的两个类。



public class OrderModel
{
public int Id { get; set; }
public string ItemCode { get; set; }
public int NumberOfItems { get; set; }
}

public class OrderDTO
{
public int Id { get; set; }
public string ItemCode { get; set; }
public int NumberOfItems { get; set; }
}


可以使用 ​​BeforeMap()​​ 在 源对象 或者 目标对象 上执行一些计算或者初始化成员操作,下面的代码展示了如何去实现。



Mapper.Initialize(cfg => {
cfg.CreateMap().BeforeMap((src, dest) => src.NumberOfItems = 0)
});


当 mapping 执行完之后,可以在 目标对象 上 安插 ​​AfterMap()​​ 方法,下面的代码展示了如何去实现。



public OrderDTO MapAuthor(IMapper mapper, OrderDTO orderDTO)
{
return mapper.Map<OrderModel, OrderDTO>(orderDTO, opt =>
{
opt.AfterMap((src, dest) =>
{
dest.NumberOfItems = _orderService.GetTotalItems(src);
});
});
}


使用嵌套映射

AutoMapper 同样也可以使用嵌套映射,考虑下面的 domain 类。



public class Order
{
public string OrderNumber { get; set; }
public IEnumerable<OrderItem> OrderItems { get; set; }
}

public class OrderItem
{
public string ItemName { get; set; }
public decimal ItemPrice { get; set; }
public int ItemQuantity { get; set; }
}


接下来再看一下 DTO 类。



public class OrderDto
{
public string OrderNumber { get; set; }
public IEnumerable<OrderItemDto> OrderItems { get; set; }
}

public class OrderItemDto
{
public string ItemName { get; set; }
public decimal ItemPrice { get; set; }
public int ItemQuantity { get; set; }
}


最后看看如何在转换的过程中使用 mapping 的。



var orders = _repository.GetOrders();
Mapper.CreateMap<Order, OrderDto>();
Mapper.CreateMap<OrderItem, OrderItemDto>();
var model = Mapper.Map<IEnumerable<Order>, IEnumerable<OrderDto>>(orders);


AutoMapper 让你用最小化的配置实现了对象之间的映射,同时也可以实现自定义的解析器来实现具有完全不同结构对象之间的映射,自定义解析器可以生成与目标对象具有相同结构的exchange,以便AutoMapper在运行时可以据其实现映射。


————————————————