• ASP.NET Core教程:ASP.NET Core使用AutoMapper


    一、前言

    在实际的项目开发过程中,我们使用各种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官网:AutoMapper — AutoMapper documentation 

    原文地址:https://www.cnblogs.com/dotnet261010/p/12494742.html

  • 相关阅读:
    AngularJS7那些不得不说的事故
    Python和C++的混合编程(使用Boost编写Python的扩展包)
    为OPENCV添加freetype支持并显示中文字符(在mac上编译opencv及contrib库)
    OpenProject基础使用介绍
    负载均衡
    如何搭建wordpress ,wecenter
    nginx 模块
    Nginx
    http 协议
    ssh
  • 原文地址:https://www.cnblogs.com/songjuntao/p/15360178.html
Copyright © 2020-2023  润新知