1. 程式人生 > 其它 >ASP.NET Core教程:ASP.NET Core使用AutoMapper

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