ASP.NET Core使用AutoMapper实现实体映射

 更新时间:2022年3月25日 14:47  点击:378 作者:.NET开发菜鸟

一、前言

在实际的项目开发过程中,我们使用各种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使用AutoMapper实现实体映射的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持猪先飞。

原文出处:https://www.cnblogs.com/dotnet261010/p/12494742.html

[!--infotagslink--]

相关文章

  • ASP.NET购物车实现过程详解

    这篇文章主要为大家详细介绍了ASP.NET购物车的实现过程,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-22
  • .NET Core下使用Kafka的方法步骤

    这篇文章主要介绍了.NET Core下使用Kafka的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • 在ASP.NET 2.0中操作数据之七十二:调试存储过程

    在开发过程中,使用Visual Studio的断点调试功能可以很方便帮我们调试发现程序存在的错误,同样Visual Studio也支持对SQL Server里面的存储过程进行调试,下面就让我们看看具体的调试方法。...2021-09-22
  • 详解.NET Core 3.0 里新的JSON API

    这篇文章主要介绍了详解.NET Core 3.0 里新的JSON API,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • ASP.NET Core根据环境变量支持多个 appsettings.json配置文件

    这篇文章主要介绍了ASP.NET Core根据环境变量支持多个 appsettings.json配置文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • 记一次EFCore类型转换错误及解决方案

    这篇文章主要介绍了记一次EFCore类型转换错误及解决方案,帮助大家更好的理解和学习使用asp.net core,感兴趣的朋友可以了解下...2021-09-22
  • 详解ASP.NET Core 中基于工厂的中间件激活的实现方法

    这篇文章主要介绍了ASP.NET Core 中基于工厂的中间件激活的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-09-22
  • asp.net通过消息队列处理高并发请求(以抢小米手机为例)

    这篇文章主要介绍了asp.net通过消息队列处理高并发请求(以抢小米手机为例),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • Underscore源码分析

    Underscore 是一个 JavaScript 工具库,它提供了一整套函数式编程的实用功能,但是没有扩展任何 JavaScript 内置对象。这篇文章主要介绍了underscore源码分析相关知识,感兴趣的朋友一起学习吧...2016-01-02
  • ASP.NET单选按钮控件RadioButton常用属性和方法介绍

    RadioButton又称单选按钮,其在工具箱中的图标为 ,单选按钮通常成组出现,用于提供两个或多个互斥选项,即在一组单选钮中只能选择一个...2021-09-22
  • ASP.NET 2.0中的数据操作:使用两个DropDownList过滤的主/从报表

    在前面的指南中我们研究了如何显示一个简单的主/从报表, 该报表使用DropDownList和GridView控件, DropDownList填充类别,GridView显示选定类别的产品. 这类报表用于显示具有...2016-05-19
  • 详解.NET Core 使用HttpClient SSL请求出错的解决办法

    这篇文章主要介绍了.NET Core 使用HttpClient SSL请求出错的解决办法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2021-09-22
  • ASP.NET中iframe框架点击左边页面链接 右边显示链接页面内容

    这篇文章主要介绍了ASP.NET中iframe框架点击左边页面链接,右边显示链接页面内容的实现代码,感兴趣的小伙伴们可以参考一下...2021-09-22
  • 创建一个完整的ASP.NET Web API项目

    ASP.NET Web API具有与ASP.NET MVC类似的编程方式,ASP.NET Web API不仅仅具有一个完全独立的消息处理管道,而且这个管道比为ASP.NET MVC设计的管道更为复杂,功能也更为强大。下面创建一个简单的Web API项目,需要的朋友可以参考下...2021-09-22
  • ASP.NET连接MySql数据库的2个方法及示例

    这篇文章主要介绍了ASP.NET连接MySql数据库的2个方法及示例,使用的是MySQL官方组件和ODBC.NET,需要的朋友可以参考下...2021-09-22
  • Asp.Net使用Bulk实现批量插入数据

    这篇文章主要介绍了Asp.Net使用Bulk实现批量插入数据的方法,对于进行asp.net数据库程序设计非常有借鉴价值,需要的朋友可以参考下...2021-09-22
  • Asp.net中获取DataTable选择第一行某一列值

    这篇文章主要介绍了获取DataTable选择第一行某一列值,需要的朋友可以参考下...2021-09-22
  • Asp.net动态生成html页面的方法分享

    这篇文章介绍了Asp.net动态生成html页面的方法,有需要的朋友可以参考一下...2021-09-22
  • ASP.Net中的async+await异步编程的实现

    这篇文章主要介绍了ASP.Net中的async+await异步编程的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • .net EF Core专题:EF Core 读取数据时发生了什么?

    这篇文章主要介绍了EF Core 读取数据的的相关知识,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2021-09-22