一、前言

在实际的项目开发过程中,我们使用各种ORM框架可以使我们快捷的获取到数据,并且可以将获取到的数据绑定到对应的List<T>中,然后页面或者接口直接显示List<T>中的数据。但是我们最终想要显示在视图或者接口中的数据和数据库实体之间可能存在着差异,一般的做法就是去创建一些对应的“模型”类,然后对获取到的数据再次进行处理,从而满足需求。

因此,如果便捷的实现数据库持久化对象与模型对象之间的实体映射,避免在去代码中手工实现这一过程,就可以大大降低开发的工作量。AutoMapper就是可以帮助我们实现实体转换过程的工具。

二、使用AutoMapper实现实体映射

AutoMapper是一个OOM(Object-Object-Mapping)组件,从它的英文名字中可以看出,AutoMapper主要是为了实现实体间的相互转换,从而避免我们每次采用手工的方式进行转换。在没有OOM这类组件之前,如果我们需要实现实体之间的转换,只能使用手工修改代码,然后逐个赋值的方式实现映射,而有了OOM组件,可以很方便的帮助我们实现这一需求。看下面的一个例子。

首先创建一个ASP.NET Core WebApi项目:

添加一个Student实体类:

namespace AutoMapperDemo.Model
{
public class Student
{
public int ID { get; set; } public string Name { get; set; } public int Age { get; set; } public string Gender { get; set; }
}
}

添加StudentDTO类,跟Student属性一致。

然后添加一个类,模拟一些测试数据:

using AutoMapperDemo.Model;
using System.Collections.Generic; namespace AutoMapperDemo
{
public class Data
{
public static List<Student> ListStudent { get; set; } public static List<Student> GetList()
{
ListStudent = new List<Student>();
for (int i = 0; i < 3; i++)
{
Student student = new Student()
{
ID=i,
Name=$"测试_{i}",
Age=20,
Gender="男"
};
ListStudent.Add(student);
} return ListStudent;
}
}
}

添加Student控制器,通过Get方法获取所有的值:

using System.Collections.Generic;
using System.Threading.Tasks;
using AutoMapperDemo.Model;
using Microsoft.AspNetCore.Mvc; namespace AutoMapperDemo.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class StudentController : ControllerBase
{
[HttpGet]
public async Task<List<Student>> Get()
{
List<Student> list = new List<Student>();
list = await Task.Run<List<Student>>(() =>
{
return Data.GetList();
});
return list;
}
}
}

使用Postman进行测试:

这样返回的数据直接就是数据库对应的实体类类型。这时需求改变了,我们要返回StudentDTO类型的数据,这时就需要修改代码:

using System.Collections.Generic;
using System.Threading.Tasks;
using AutoMapperDemo.DTO;
using AutoMapperDemo.Model;
using Microsoft.AspNetCore.Mvc; namespace AutoMapperDemo.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class StudentController : ControllerBase
{
[HttpGet]
public async Task<List<Student>> Get()
{
List<Student> list = new List<Student>();
list = await Task.Run<List<Student>>(() =>
{
return Data.GetList();
}); return list;
} [HttpGet("GetDTO")]
public async Task<List<StudentDTO>> GetDto()
{
List<StudentDTO> list = new List<StudentDTO>();
List<Student> listStudent = await Task.Run<List<Student>>(() =>
{
return Data.GetList();
});
// 循环给属性赋值
foreach (var item in listStudent)
{
StudentDTO dto = new StudentDTO();
dto.ID = item.ID;
dto.Name = item.Name;
dto.Age = item.Age;
dto.Gender = item.Gender;
// 加入到集合中
list.Add(dto);
} return list;
}
}
}

还是使用Postman进行测试:

可以看到:这时返回的是DTO类型的数据。这种情况就是我们上面说的,需要手动修改代码,然后循环给对应的属性进行赋值。这里Student类只有4个属性,如果属性非常多,或者很多地方使用到了,如果还是采用这种方式进行赋值,那么就会很麻烦。假如以后其中的一个属性名称改变了,那么所有的地方也都需要修改,工作量就会很大。这时就需要使用AutoMapper解决。

首先引入AutoMapper包,直接在NuGet中引入:

这里选择安装AutoMapper.Extensions.Microsoft.DependencyInjection这个包。这个包主要是为了让我们可以通过依赖注入的方式去使用AutoMapper。

新建StudentProfile类,继承自AutoMapper的Profile类,在无参构造函数中,我们就可以通过 CreateMap 方法去创建两个实体间的映射关系。

using AutoMapper;
using AutoMapperDemo.DTO;
using AutoMapperDemo.Model; namespace AutoMapperDemo.AutoMapper
{
/// <summary>
/// 继承自Profile类
/// </summary>
public class StudentProfile: Profile
{
/// <summary>
/// 构造函数中实现映射
/// </summary>
public StudentProfile()
{
// Mapping
// 第一次参数是源类型(这里是Model类型),第二个参数是目标类型(这里是DTO类型)
CreateMap<Student, StudentDTO>();
}
}
}

这里的 Profile有什么用呢?services.AddAutoMapper他会自动找到所有继承了Profile的类然后进行配置。

然后修改Student控制器,通过构造函数使用AutoMapper的注入,并使用AutoMapper实现自动映射:

using System.Collections.Generic;
using System.Threading.Tasks;
using AutoMapper;
using AutoMapperDemo.DTO;
using AutoMapperDemo.Model;
using Microsoft.AspNetCore.Mvc; namespace AutoMapperDemo.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class StudentController : ControllerBase
{
private readonly IMapper _mapper; /// <summary>
/// 通过构造函数实现依赖注入
/// </summary>
/// <param name="mapper"></param>
public StudentController(IMapper mapper)
{
_mapper = mapper;
} [HttpGet]
public async Task<List<Student>> Get()
{
List<Student> list = new List<Student>();
list = await Task.Run<List<Student>>(() =>
{
return Data.GetList();
}); return list;
} [HttpGet("GetDTO")]
public async Task<List<StudentDTO>> GetDto()
{
List<StudentDTO> list = new List<StudentDTO>();
List<Student> listStudent = await Task.Run<List<Student>>(() =>
{
return Data.GetList();
});
//// 循环给属性赋值
//foreach (var item in listStudent)
//{
// StudentDTO dto = new StudentDTO();
// dto.ID = item.ID;
// dto.Name = item.Name;
// dto.Age = item.Age;
// dto.Gender = item.Gender;
// // 加入到集合中
// list.Add(dto);
//} // 使用AutoMapper进行映射
list = _mapper.Map<List<StudentDTO>>(listStudent);
return list;
}
}
}

修改Startup类的ConfigureServices方法,添加AutoMapper:

public void ConfigureServices(IServiceCollection services)
{
#region 使用AutoMapper
// 参数类型是Assembly类型的数组 表示AutoMapper将在这些程序集数组里面遍历寻找所有继承了Profile类的配置文件
// 在当前作用域的所有程序集里面扫描AutoMapper的配置文件
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
#endregion services.AddControllers();
}

再次使用Postman进行测试:

可以看到,这样也实现了我们的需求,而且还不需要进行手动映射。

上面的示例中,Student和StudentDTO类里面的属性名称都是一样的,如果属性名称不一样呢?我们把StudentDTO类里面的ID改为StudentID,然后修改映射代码:

using AutoMapper;
using AutoMapperDemo.DTO;
using AutoMapperDemo.Model; namespace AutoMapperDemo.AutoMapper
{
/// <summary>
/// 继承自Profile类
/// </summary>
public class StudentProfile: Profile
{
/// <summary>
/// 构造函数中实现映射
/// </summary>
public StudentProfile()
{
// Mapping
// 第一次参数是源类型(这里是Model类型),第二个参数是目标类型(这里是DTO类型)
// CreateMap<Student, StudentDTO>(); // 使用自定义映射 Student类的ID映射到StudentDTO类的StudentID
CreateMap<Student, StudentDTO>()
.ForMember(destinationMember: des => des.StudentID, memberOptions: opt => { opt.MapFrom(mapExpression: map => map.ID); });
}
}
}

再次使用Postman进行测试:

这样就实现了自定义映射。这里是映射了一个字段,如果是多个字段不同呢? 修改StudentDTO类:

namespace AutoMapperDemo.DTO
{
public class StudentDTO
{
public int StudentID { get; set; } public string StudentName { get; set; } public int StudentAge { get; set; } public string StudentGender { get; set; }
}
}

然后修改映射配置类:

using AutoMapper;
using AutoMapperDemo.DTO;
using AutoMapperDemo.Model; namespace AutoMapperDemo.AutoMapper
{
/// <summary>
/// 继承自Profile类
/// </summary>
public class StudentProfile: Profile
{
/// <summary>
/// 构造函数中实现映射
/// </summary>
public StudentProfile()
{
// Mapping
// 第一次参数是源类型(这里是Model类型),第二个参数是目标类型(这里是DTO类型)
// CreateMap<Student, StudentDTO>(); // 使用自定义映射 Student类的ID映射到StudentDTO类的StudentID
//CreateMap<Student, StudentDTO>()
// .ForMember(destinationMember: des => des.StudentID, memberOptions: opt => { opt.MapFrom(mapExpression: map => map.ID); }); // 对多个属性进行自定义映射
CreateMap<Student, StudentDTO>()
.ForMember(destinationMember: des => des.StudentID, memberOptions: opt => { opt.MapFrom(mapExpression: map => map.ID); })
.ForMember(destinationMember: des => des.StudentName, memberOptions: opt => { opt.MapFrom(mapExpression: map => map.Name); })
.ForMember(destinationMember: des => des.StudentAge, memberOptions: opt => { opt.MapFrom(mapExpression: map => map.Age); })
.ForMember(destinationMember: des => des.StudentGender, memberOptions: opt => { opt.MapFrom(mapExpression: map => map.Gender); });
}
}
}

在使用Postman进行测试:

这样就实现了多个属性的自定义映射。

上面的实例中是从Student映射到StudentDTO,那么可以从StudentDTO映射到Student吗?答案是肯定的,只需要在映射的最后使用ReverseMap()方法即可:

using AutoMapper;
using AutoMapperDemo.DTO;
using AutoMapperDemo.Model; namespace AutoMapperDemo.AutoMapper
{
/// <summary>
/// 继承自Profile类
/// </summary>
public class StudentProfile: Profile
{
/// <summary>
/// 构造函数中实现映射
/// </summary>
public StudentProfile()
{
// Mapping
// 第一次参数是源类型(这里是Model类型),第二个参数是目标类型(这里是DTO类型)
// CreateMap<Student, StudentDTO>(); // 使用自定义映射 Student类的ID映射到StudentDTO类的StudentID
//CreateMap<Student, StudentDTO>()
// .ForMember(destinationMember: des => des.StudentID, memberOptions: opt => { opt.MapFrom(mapExpression: map => map.ID); }); // 对多个属性进行自定义映射
CreateMap<Student, StudentDTO>()
.ForMember(destinationMember: des => des.StudentID, memberOptions: opt => { opt.MapFrom(mapExpression: map => map.ID); })
.ForMember(destinationMember: des => des.StudentName, memberOptions: opt => { opt.MapFrom(mapExpression: map => map.Name); })
.ForMember(destinationMember: des => des.StudentAge, memberOptions: opt => { opt.MapFrom(mapExpression: map => map.Age); })
.ForMember(destinationMember: des => des.StudentGender, memberOptions: opt => { opt.MapFrom(mapExpression: map => map.Gender); })
// ReverseMap表示双向映射
.ReverseMap();
}
}
}

我们修改Data,里面增加一个Add方法,可以将Student添加到集合中:

using AutoMapperDemo.Model;
using System.Collections.Generic; namespace AutoMapperDemo
{
public class Data
{
public static List<Student> ListStudent { get; set; } static Data()
{
ListStudent = new List<Student>();
for (int i = 0; i < 3; i++)
{
Student student = new Student()
{
ID = i,
Name = $"测试_{i}",
Age = 20,
Gender = "男"
};
ListStudent.Add(student);
}
} public static List<Student> GetList()
{
return ListStudent;
} public static void Add(Student entity)
{
ListStudent.Add(entity);
}
}
}

修改Student控制器,添加一个Post方法,传入的参数的StudentDTO类型:

using System.Collections.Generic;
using System.Threading.Tasks;
using AutoMapper;
using AutoMapperDemo.DTO;
using AutoMapperDemo.Model;
using Microsoft.AspNetCore.Mvc; namespace AutoMapperDemo.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class StudentController : ControllerBase
{
private readonly IMapper _mapper; /// <summary>
/// 通过构造函数实现依赖注入
/// </summary>
/// <param name="mapper"></param>
public StudentController(IMapper mapper)
{
_mapper = mapper;
} [HttpGet]
public async Task<List<Student>> Get()
{
List<Student> list = new List<Student>();
list = await Task.Run<List<Student>>(() =>
{
return Data.GetList();
}); return list;
} [HttpGet("GetDTO")]
public async Task<List<StudentDTO>> GetDto()
{
List<StudentDTO> list = new List<StudentDTO>();
List<Student> listStudent = await Task.Run<List<Student>>(() =>
{
return Data.GetList();
});
//// 循环给属性赋值
//foreach (var item in listStudent)
//{
// StudentDTO dto = new StudentDTO();
// dto.ID = item.ID;
// dto.Name = item.Name;
// dto.Age = item.Age;
// dto.Gender = item.Gender;
// // 加入到集合中
// list.Add(dto);
//} // 使用AutoMapper进行映射
list = _mapper.Map<List<StudentDTO>>(listStudent);
return list;
} [HttpPost]
public async Task<List<Student>> Post([FromBody]StudentDTO entity)
{
List<Student> list = new List<Student>();
// 将StudentDTO反向映射为Student类型
Student student = _mapper.Map<Student>(entity);
// 添加到集合中
Data.Add(student);
// 返回增加后的数组,这里返回Student
list = await Task.Run<List<Student>>(() =>
{
return Data.GetList();
}); return list;
}
}
}

使用Postman进行测试:

返回结果:

这样就实现了映射的反转。

具体其它API功能,参考AutoMapper官网:https://automapper.readthedocs.io/en/latest/index.html

ASP.NET Core教程:ASP.NET Core使用AutoMapper的更多相关文章

  1. 基于Asp.Net Core Mvc和EntityFramework Core 的实战入门教程系列-1

    来个目录吧: 第一章 第二章 第三章 暂时就这么多.后面路线更新吧 本系列文章为翻译加上我个人的使用心得理解,希望帮助热爱学习的程序员. 珍重声明:本系列文章会跟原文有点出入,去掉了罗里吧嗦的文字. ...

  2. 003.ASP.NET Core tutorials--【Asp.net core 教程】

    ASP.NET Core tutorials Asp.net core 教程 2016-10-14 1 分钟阅读时长 本文内容 1.Building web applications 构建web应用 ...

  3. ASP.NET Core教程【二】从保存数据看特有属性与服务端验证

    前文索引: 在layout.cshtml文件中,我们可以看到如下代码: <a asp-page="/Index" class="navbar-brand" ...

  4. ASP.NET Core教程【三】实体字段属性、链接标签、并发数据异常、文件上传及读取

    前文索引:ASP.NET Core教程[二]从保存数据看Razor Page的特有属性与服务端验证ASP.NET Core教程[一]关于Razor Page的知识 实体字段属性 再来看看我们的实体类 ...

  5. ASP.NET Core教程【二】从保存数据看Razor Page的特有属性与服务端验证

    前文索引:ASP.NET Core教程[一]关于Razor Page的知识 在layout.cshtml文件中,我们可以看到如下代码: <a asp-page="/Index" ...

  6. C# -- HttpWebRequest 和 HttpWebResponse 的使用 C#编写扫雷游戏 使用IIS调试ASP.NET网站程序 WCF入门教程 ASP.Net Core开发(踩坑)指南 ASP.Net Core Razor+AdminLTE 小试牛刀 webservice创建、部署和调用 .net接收post请求并把数据转为字典格式

    C# -- HttpWebRequest 和 HttpWebResponse 的使用 C# -- HttpWebRequest 和 HttpWebResponse 的使用 结合使用HttpWebReq ...

  7. ASP.NET Core MVC 2.x 全面教程_ASP.NET Core MVC 03. 服务注册和管道

    ASP.NET Core MVC 2.x 全面教程_ASP.NET Core MVC 03. 服务注册和管道 语雀: https://www.yuque.com/yuejiangliu/dotnet/ ...

  8. ASP.NET Core 用户注册 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core 用户注册 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 用户注册 上一章节我们终于迁移完了 Identity 的数据,也创建 ...

  9. ASP.NET Core 配置 Entity Framework Core - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core 配置 Entity Framework Core - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 配置 Entity Fram ...

  10. ASP.NET Core 视图 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core 视图 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 视图 花了几章节,终于把 ASP.NET Core MVC 中的 C 控 ...

随机推荐

  1. C语言:变量定义

    变量定义:用于为变量分配存储空间,还可为变量指定初始值.程序中,变量有且仅有一个定义.变量声明:用于向程序表明变量的类型和名字.定义也是声明,extern声明不是定义 定义也是声明:当定义变量时我们声 ...

  2. Wordcloud(词云)安装使用以及vscode搭建虚拟环境

    (电脑烧掉了主板,地方上的所有门店全部关闭了,幸好现在京东还通物流,总算是进行把电脑拿回来了.对于一些东西无法实际操作真的是很难受,言归正传,说一下Wordcloud) Wordcloud安装(全局安 ...

  3. Spark—RDD介绍

    Spark-RDD 1.概念介绍 RDD(Resilient Distributed Dataset):弹性分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变.可分区.里面的元素可并行计算 ...

  4. Linux从入门到进阶全集——【第十五集:安装apache服务器】

    1,查看是否安装了httpd软件包以及其依赖:rpm -qa httpd(rpm -qa | grep httpd),如果没有输出任何信息,表示你没有安装httpd软件包,如果有输出一般是已经安装了: ...

  5. 【spring源码系列】之【Bean的循环依赖】

    希望之光永远向着目标清晰的人敞开. 1. 循环依赖概述 循环依赖通俗讲就是循环引用,指两个或两个以上对象的bean相互引用对方,A依赖于B,B依赖于A,最终形成一个闭环. Spring循环依赖的场景有 ...

  6. 使用宝塔配置laravel站点时,遇到open_basedir restriction in effect. 原因与解决方法

    今天一位朋友在linux服务器部署thinkphp5的时候PHP报了这个错误,如下: Warning: require(): open_basedir restriction in effect. F ...

  7. 利用 PGO 提升 .NET 程序性能

    引子 .NET 6 开始初步引入 PGO.PGO 即 Profile Guided Optimization,通过收集运行时信息来指导 JIT 如何优化代码,相比以前没有 PGO 时可以做更多以前难以 ...

  8. python读取csv,Excel,Txt,Yaml 文件

    1.数据 1.Csv login.csv文件: byhy,88888888 ReadCsv.py文件 import csv #导入csv包 class ReadCsv(): def csv(self) ...

  9. odoo里的开发案例

    1.模块命名[驼峰命名方法] res开头的是:resources   常见模型:res.users,   res.company,    res.partner,   res.config.setti ...

  10. Vue 2升级 Vue 3初探小细节

    前言 嗯,偶尔看看学习Vue 3技能啦,此前用过Vue 2做过一点东西,Vue 3已面世一段时间,于是乎,我来看看所遇到的问题是否在Vue 3中得到解决,首先,我们来讲讲一个例子在Vue 2中的实现, ...