ASP.NET Core使用AutoMapper實現(xiàn)實體映射
一、前言
在實際的項目開發(fā)過程中,我們使用各種ORM框架可以使我們快捷的獲取到數(shù)據(jù),并且可以將獲取到的數(shù)據(jù)綁定到對應(yīng)的List<T>中,然后頁面或者接口直接顯示List<T>中的數(shù)據(jù)。但是我們最終想要顯示在視圖或者接口中的數(shù)據(jù)和數(shù)據(jù)庫實體之間可能存在著差異,一般的做法就是去創(chuàng)建一些對應(yīng)的“模型”類,然后對獲取到的數(shù)據(jù)再次進行處理,從而滿足需求。
因此,如果便捷的實現(xiàn)數(shù)據(jù)庫持久化對象與模型對象之間的實體映射,避免在去代碼中手工實現(xiàn)這一過程,就可以大大降低開發(fā)的工作量。AutoMapper就是可以幫助我們實現(xiàn)實體轉(zhuǎn)換過程的工具。
二、使用AutoMapper實現(xiàn)實體映射
AutoMapper是一個OOM(Object-Object-Mapping)組件,從它的英文名字中可以看出,AutoMapper主要是為了實現(xiàn)實體間的相互轉(zhuǎn)換,從而避免我們每次采用手工的方式進行轉(zhuǎn)換。在沒有OOM這類組件之前,如果我們需要實現(xiàn)實體之間的轉(zhuǎn)換,只能使用手工修改代碼,然后逐個賦值的方式實現(xiàn)映射,而有了OOM組件,可以很方便的幫助我們實現(xiàn)這一需求。看下面的一個例子。
首先創(chuàng)建一個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屬性一致。
然后添加一個類,模擬一些測試數(shù)據(jù):
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進行測試:

這樣返回的數(shù)據(jù)直接就是數(shù)據(jù)庫對應(yīng)的實體類類型。這時需求改變了,我們要返回StudentDTO類型的數(shù)據(jù),這時就需要修改代碼:
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();
});
// 循環(huán)給屬性賦值
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類型的數(shù)據(jù)。這種情況就是我們上面說的,需要手動修改代碼,然后循環(huán)給對應(yīng)的屬性進行賦值。這里Student類只有4個屬性,如果屬性非常多,或者很多地方使用到了,如果還是采用這種方式進行賦值,那么就會很麻煩。假如以后其中的一個屬性名稱改變了,那么所有的地方也都需要修改,工作量就會很大。這時就需要使用AutoMapper解決。
首先引入AutoMapper包,直接在NuGet中引入:

這里選擇安裝AutoMapper.Extensions.Microsoft.DependencyInjection這個包。這個包主要是為了讓我們可以通過依賴注入的方式去使用AutoMapper。
新建StudentProfile類,繼承自AutoMapper的Profile類,在無參構(gòu)造函數(shù)中,我們就可以通過 CreateMap 方法去創(chuàng)建兩個實體間的映射關(guān)系。
using AutoMapper;
using AutoMapperDemo.DTO;
using AutoMapperDemo.Model;
namespace AutoMapperDemo.AutoMapper
{
/// <summary>
/// 繼承自Profile類
/// </summary>
public class StudentProfile: Profile
{
/// <summary>
/// 構(gòu)造函數(shù)中實現(xiàn)映射
/// </summary>
public StudentProfile()
{
// Mapping
// 第一次參數(shù)是源類型(這里是Model類型),第二個參數(shù)是目標(biāo)類型(這里是DTO類型)
CreateMap<Student, StudentDTO>();
}
}
}這里的 Profile有什么用呢?services.AddAutoMapper他會自動找到所有繼承了Profile的類然后進行配置。
然后修改Student控制器,通過構(gòu)造函數(shù)使用AutoMapper的注入,并使用AutoMapper實現(xiàn)自動映射:
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>
/// 通過構(gòu)造函數(shù)實現(xiàn)依賴注入
/// </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();
});
//// 循環(huán)給屬性賦值
//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
// 參數(shù)類型是Assembly類型的數(shù)組 表示AutoMapper將在這些程序集數(shù)組里面遍歷尋找所有繼承了Profile類的配置文件
// 在當(dāng)前作用域的所有程序集里面掃描AutoMapper的配置文件
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
#endregion
services.AddControllers();
}再次使用Postman進行測試:

可以看到,這樣也實現(xiàn)了我們的需求,而且還不需要進行手動映射。
上面的示例中,Student和StudentDTO類里面的屬性名稱都是一樣的,如果屬性名稱不一樣呢?我們把StudentDTO類里面的ID改為StudentID,然后修改映射代碼:
using AutoMapper;
using AutoMapperDemo.DTO;
using AutoMapperDemo.Model;
namespace AutoMapperDemo.AutoMapper
{
/// <summary>
/// 繼承自Profile類
/// </summary>
public class StudentProfile: Profile
{
/// <summary>
/// 構(gòu)造函數(shù)中實現(xiàn)映射
/// </summary>
public StudentProfile()
{
// Mapping
// 第一次參數(shù)是源類型(這里是Model類型),第二個參數(shù)是目標(biāo)類型(這里是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進行測試:

這樣就實現(xiàn)了自定義映射。這里是映射了一個字段,如果是多個字段不同呢? 修改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>
/// 構(gòu)造函數(shù)中實現(xiàn)映射
/// </summary>
public StudentProfile()
{
// Mapping
// 第一次參數(shù)是源類型(這里是Model類型),第二個參數(shù)是目標(biāo)類型(這里是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進行測試:

這樣就實現(xiàn)了多個屬性的自定義映射。
上面的實例中是從Student映射到StudentDTO,那么可以從StudentDTO映射到Student嗎?答案是肯定的,只需要在映射的最后使用ReverseMap()方法即可:
using AutoMapper;
using AutoMapperDemo.DTO;
using AutoMapperDemo.Model;
namespace AutoMapperDemo.AutoMapper
{
/// <summary>
/// 繼承自Profile類
/// </summary>
public class StudentProfile: Profile
{
/// <summary>
/// 構(gòu)造函數(shù)中實現(xiàn)映射
/// </summary>
public StudentProfile()
{
// Mapping
// 第一次參數(shù)是源類型(這里是Model類型),第二個參數(shù)是目標(biāo)類型(這里是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方法,傳入的參數(shù)的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>
/// 通過構(gòu)造函數(shù)實現(xiàn)依賴注入
/// </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();
});
//// 循環(huán)給屬性賦值
//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);
// 返回增加后的數(shù)組,這里返回Student
list = await Task.Run<List<Student>>(() =>
{
return Data.GetList();
});
return list;
}
}
}使用Postman進行測試:

返回結(jié)果:

這樣就實現(xiàn)了映射的反轉(zhuǎn)。
具體其它API功能,參考AutoMapper官網(wǎng):https://automapper.readthedocs.io/en/latest/index.html
到此這篇關(guān)于ASP.NET Core使用AutoMapper實現(xiàn)實體映射的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
淺談ASP.NET Core中間件實現(xiàn)分布式 Session
這篇文章主要介紹了淺談ASP.NET Core中間件實現(xiàn)分布式 Session,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-11-11
Visual Studio Debugger七個鮮為人知的小功能
這篇文章主要為大家詳細(xì)介紹了Visual Studio Debugger七個鮮為人知的小功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06
ASP.NET MVC小結(jié)之基礎(chǔ)篇(一)
本文是ASP.NET MVC系列的第一篇文章,跟其他學(xué)習(xí)系列一樣,咱們先來點基礎(chǔ)知識,之后再循序漸進。我們先從asp.net mvc的概念開始吧。2014-11-11

