ASP.NET Core 中的对象映射之 AutoMapper
AutoMapper 简介
AutoMapper是一个对象映射器,它可以将一种类型的对象转换为另一种类型的对象。
它提供了映射规则及操作方法,使我们不用过多配置就可以映射两个类, 可以帮我们免于编写无聊的映射代码. 在代码层与层之间隔离模型model上非常有用.
AutoMapper 使用
初始化
创建两个简单的类用于测试:
public class UserEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
}
public class UserDTO
{
    public int Id { get; set; }
    public string Name { get; set; }
}
AutoMapper可以使用静态类和实例方法来创建映射.
静态类方式
Mapper.Initialize(cfg => cfg.CreateMap<UserEntity, UserDTO>());
var userDTO = Mapper.Map<UserDTO>(user);
实例方式
var config = new MapperConfiguration(cfg => cfg.CreateMap<UserEntity, UserDTO>());
var mapper = config.CreateMapper(); var userDTO = mapper.Map<UserDTO>(user);
依赖注入
使用扩展 AutoMapper.Extensions.Microsoft.DependencyInjection 来实现AutoMapper的依赖注入. 本质是注册一个MapperConfiguration的单例和IMapper的scope实例, 通过程序集扫描添加AutoMapper的相关配置和映射.
IServiceCollection services = new ServiceCollection();
services.AddAutoMapper(); var provider = services.BuildServiceProvider();
using (var scope = provider.CreateScope())
{
var mapper = scope.ServiceProvider.GetService<IMapper>();
var userDTO = mapper.Map<UserDTO>(user);
}
Profile设置
可以使用Profie配置来实现映射关系, 然后通过AddProfile添加.
public class UserProfile : Profile
{
    public UserProfile()
    {
        CreateMap<UserEntity, UserDTO>();
    }
}
var config = new MapperConfiguration(cfg => cfg.AddProfile<UserProfile>());
扁平化映射
AutoMapper支持扁平化映射, 它会根据Pascal命名方式分割目标字段为单个单词, 可自动映射属性名+内嵌属性名. 如下例AutoMapper自动映射UserEntity.Address.City -> UserDTO.AddressCity。
public class UserEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Address Address { get; set; }
}
public class Address
{
    public string City { get; set; }
    public string Country { get; set; }
}
public class UserDTO
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string AddressCity { get; set; }
    public string AddressCountry { get; set; }
}
集合映射
AutoMapper除了可以映射单个对象外,也可以映射集合对象。
CreateMap<UserEntity, UserDTO>();
var userList = new List<UserEntity> {
    new UserEntity { Id = 1, Name="Test1" },
    new UserEntity { Id = 2, Name="Test2" },
};
var dtoList = mapper.Map<List<UserDTO>>(userList);
public class UserEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<AddressEntity> AddressList { get; set; }
}
public class AddressEntity
{
    public string City { get; set; }
    public string Country { get; set; }
}
public class UserDTO
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<AddressDTO> AddressList { get; set; }
}
public class AddressDTO
{
    public string City { get; set; }
    public string Country { get; set; }
}
CreateMap<AddressEntity, AddressDTO>();
CreateMap<UserEntity, UserDTO>();
var user = new UserEntity
{
    Id = 1,
    Name = "Test",
    AddressList = new List<AddressEntity>
    {
        new AddressEntity { City = "ShangHai", Country = "China"},
        new AddressEntity { City = "BeiJing", Country = "China"}
    }
};
var userDTO = mapper.Map<UserDTO>(user);
投影
当把一个源值投影到一个不精准匹配源结构的目标值时,使用MapFrom指明成员映射定义。
public class UserEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime BirthDate { get; set; }
}
public class UserDTO
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string BirthYear { get; set; }
    public string BirthMonth { get; set; }
}
public class UserProfile : Profile
{
    public UserProfile()
    {
        CreateMap<UserEntity, UserDTO>()
            .ForMember(d => d.BirthYear, o => o.MapFrom(s => s.BirthDate.Year))
            .ForMember(d => d.BirthMonth, o => o.MapFrom(s => s.BirthDate.Month));
    }
}
var user = new UserEntity
{
    Id = 1,
    Name = "Test",
    BirthDate = DateTime.Today,
};
var userDTO = mapper.Map<UserDTO>(user);
条件映射
有些情况下,我们将只满足映射条件的才添加到属性上.
public class UserProfile : Profile
{
    public UserProfile()
    {
        CreateMap<UserEntity, UserDTO>()
            .ForMember(d => d.Id, o => o.Condition(s => s.Id > 1));
    }
}
值转换
AutoMapper可以配置值转换和空值替换
public class UserProfile : Profile
{
    public UserProfile()
    {
        CreateMap<UserEntity, UserDTO>()
            .ForMember(d => d.Name, o => o.NullSubstitute("Default Name"))
            .ForMember(d => d.Name, o => o.AddTransform(val => string.Format("Name: {0}", val)));
    }
}
设置转换前后行为
有时候,在映射发生之前或之后,可能需要执行一些自定义的逻辑。
public class UserProfile : Profile
{
    public UserProfile()
    {
        CreateMap<UserEntity, UserDTO>()
            .BeforeMap((s, d) => s.BirthDate = s.BirthDate.AddYears(-12))
            .AfterMap((s, d) => d.BirthMonth = "July");
    }
}
配置验证及设置
配置了映射,但是如何确定是否映射成功或者是否有字段没有映射呢?可以使用mapper.ConfigurationProvider.AssertConfigurationIsValid()来验证是否映射成功。但也可以指定单个字段不验证.
public class UserProfile : Profile
{
    public UserProfile()
    {
        CreateMap<UserEntity, UserDTO>()
            .ForMember(d => d.NickName, o => o.Ignore());
    }
}
反向映射
从6.1.0开始,AutoMapper通过ReverseMap可以实现反向映射。使用ReverseMap, 不用再创建DTO -> Entity的映射, 而且还能保留正向的映射规则。
public class UserProfile : Profile
{
    public UserProfile()
    {
        CreateMap<UserEntity, UserDTO>()
            .ReverseMap();
    }
}
自定义转换器
有些情况下目标字段类型和源字段类型不一致,可以通过类型转换器实现映射,类型转换器有三种实现方式:
void ConvertUsing(Func<TSource, TDestination> mappingFunction);
void ConvertUsing(ITypeConverter<TSource, TDestination> converter);
void ConvertUsing<TTypeConverter>() where TTypeConverter : ITypeConverter<TSource, TDestination>;
自定义解析器
某些情况下,解析规则会很复杂,使用自带的解析规则无法实现。这时可以自定义解析规则,可以通过以下三种方式使用自定义的解析器:
ResolveUsing<TValueResolver>
ResolveUsing(typeof(CustomValueResolver))
ResolveUsing(aValueResolverInstance)
public class UserEntity
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
public class UserDTO
{
    public string Name { get; set; }
}
public class UserNameResolver : IValueResolver<UserEntity, UserDTO, string>
{
    public string Resolve(UserEntity source, UserDTO destination, string destMember, ResolutionContext context)
    {
        if (source != null && !string.IsNullOrEmpty(source.FirstName) && !string.IsNullOrEmpty(source.LastName))
        {
            return string.Format("{0} {1}", source.FirstName, source.LastName);
        }
        return string.Empty;
    }
}
public class UserProfile : Profile
{
    public UserProfile()
    {
        CreateMap<UserEntity, UserDTO>()
            .ForMember(d => d.Name, o => o.ResolveUsing<UserNameResolver>());
    }
}
参考
ASP.NET Core 中的对象映射之 AutoMapper的更多相关文章
- 在Asp .net core 中通过属性映射实现动态排序和数据塑形
		
目录 属性映射服务实现 动态排序 数据塑形 属性映射服务实现 public class PropertyMappingValue { public IEnumerable<string> ...
 - ASP.NET CORE 中使用AutoMapper进行对象映射
		
ASP.NET CORE 中使用AutoMapper进行对象映射 1.什么是AutoMapper? AutoMapper是基于对象到对象约定的映射工具,常用于(但并不仅限制于)把复杂的对象模型转为DT ...
 - 项目开发中的一些注意事项以及技巧总结  基于Repository模式设计项目架构—你可以参考的项目架构设计  Asp.Net Core中使用RSA加密   EF Core中的多对多映射如何实现?  asp.net core下的如何给网站做安全设置  获取服务端https证书  Js异常捕获
		
项目开发中的一些注意事项以及技巧总结 1.jquery采用ajax向后端请求时,MVC框架并不能返回View的数据,也就是一般我们使用View().PartialView()等,只能返回json以 ...
 - ASP.NET Core Web 应用程序系列(五)- 在ASP.NET Core中使用AutoMapper进行实体映射
		
本章主要简单介绍下在ASP.NET Core中如何使用AutoMapper进行实体映射.在正式进入主题之前我们来看下几个概念: 1.数据库持久化对象PO(Persistent Object):顾名思义 ...
 - ASP.NET Core中如何针对一个使用HttpClient对象的类编写单元测试
		
原文地址: How to unit test a class that consumes an HttpClient with IHttpClientFactory in ASP.NET Core? ...
 - ASP.NET Core中的依赖注入(2):依赖注入(DI)
		
IoC主要体现了这样一种设计思想:通过将一组通用流程的控制从应用转移到框架之中以实现对流程的复用,同时采用"好莱坞原则"是应用程序以被动的方式实现对流程的定制.我们可以采用若干设计 ...
 - ASP.NET Core中的依赖注入(3): 服务的注册与提供
		
在采用了依赖注入的应用中,我们总是直接利用DI容器直接获取所需的服务实例,换句话说,DI容器起到了一个服务提供者的角色,它能够根据我们提供的服务描述信息提供一个可用的服务对象.ASP.NET Core ...
 - ASP.NET Core 中文文档 第三章 原理(13)管理应用程序状态
		
原文:Managing Application State 作者:Steve Smith 翻译:姚阿勇(Dr.Yao) 校对:高嵩 在 ASP.NET Core 中,有多种途径可以对应用程序的状态进行 ...
 - ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【总体设计 】
		
本系列前面的文章我们主要以编程的角度对ASP.NET Core的依赖注入系统进行了详细的介绍,如果读者朋友们对这些内容具有深刻的理解,我相信你们已经可以正确是使用这些与依赖注入相关的API了.如果你还 ...
 
随机推荐
- 图书助手Alpha版使用说明
			
一.产品介绍 我们做的是一个基于安卓的手机app,通过连接图书馆的数据库,实现查询图书馆的书目信息的功能. 二.软件运行 我们只做了安卓版本,需要在安卓环境下运行. 三.软件结构 本软件主要包括客户端 ...
 - WinRT 中后台任务类的声明
			
要实现后台任务,需要实现IBackgroundTask接口 public sealed class SimpleTask : IBackgroundTask { public void Run(IBa ...
 - Java泛型与Restlet客户端
			
写一个与restlet服务器通信的客户端类,用于测试通信是否成功,并且进行交互.为了方便其他人使用,于是,写一个通用的方法封装起来,可是中途却放生了一些问题. 按照正常写法,顺序走下来是这样的: pu ...
 - 实现liunx之间无密码访问——ssh密匙
			
环境描述 两台linux服务器 172.16.1.22[client],172.16.1.33[server],想要实现client服务器ssh无密码访问server服务器. 使用技术 linux 的 ...
 - S11 day 97 -98天  Luffycity项目
			
1. 建模 from django.db import models from django.contrib.contenttypes.fields import GenericForeignKey, ...
 - 初识面向对象-封装、property装饰器、staticmathod(静态的方法)、classmethod(类方法) (五)
			
封装 # class Room:# def __init__(self,name,length,width):# self.__name = name# self.__length = length# ...
 - 栈的实现——java
			
和C++一样,JDK包中也提供了"栈"的实现,它就是集合框架中的Stack类.关于Stack类的原理,在"Java 集合系列07之 Stack详细介绍(源码解析)和使用示 ...
 - ZZNU 2055(基姆拉尔森计算公式)
			
题目链接 题意: 比如今天是2017年8月16日,星期三.下一个也是星期三的8月16日发生在2023年. 现在是日期是yyyy-mm-dd,我们希望你求出薛定谔会跳跃到那一年. 题解: emmmm.. ...
 - 频繁项集挖掘之Aprior和FPGrowth算法
			
频繁项集挖掘的应用多出现于购物篮分析,现介绍两种频繁项集的挖掘算法Aprior和FPGrowth,用以发现购物篮中出现频率较高的购物组合. 基础知识 项:“属性-值”对.比如啤酒2罐. 项集:项的集 ...
 - iOS开发总结——协议代理的认识
			
1.前言 自今年5月底正式转iOS之后,天天get新技能,很多技能在脑子里回旋不吐不快,所以,写点东西整理一下.先从协议代理开始. 2.协议方法的声明 @protocol EventMenuBarDe ...