十年河東,十年河西,莫欺少年窮。
學無止境,精益求精。
不扯犢子,直接進入正題:
AutoMapper自動映射常用於EF中,能很好的解決DTO和Model之間相互映射的問題。在未使用AutoMapper之前,我們回顧下傳統的對象相互映射的方法。
首先貼出本節要用到的DTO,學生表及系表,他們之間存在主外鍵關系!如下:

public partial class Dept { public Dept() { this.Student = new HashSet<Student>(); } public int Id { get; set; } public string deptNum { get; set; } public string deptName { get; set; } public virtual ICollection<Student> Student { get; set; } } public partial class Student { public int Id { get; set; } public string StuNum { get; set; } public string deptNum { get; set; } public string StuName { get; set; } public string StuSex { get; set; } public Nullable<System.DateTime> AddTime { get; set; } public virtual Dept Dept { get; set; } }
假設,現在要求將得到的學生對象轉化為Model
新建學生對象Model

public class StudentModel { public int Id { get; set; } public string StuNum { get; set; } public string deptNum { get; set; } public string StuName { get; set; } public string StuSex { get; set; } public Nullable<System.DateTime> AddTime { get; set; } }
傳統方法如下:

public ActionResult Index() { var profiler = MiniProfiler.Current; using (profiler.Step("查詢Student的數據")) { using (BingFaTestEntities context = new BingFaTestEntities()) { var SM = context.Student.Where(A => A.StuNum == "0813091000000").FirstOrDefault(); if (SM != null) { var StudetM = new StudentModel() { Id = SM.Id, StuName = SM.StuNum, StuNum = SM.StuNum, StuSex = SM.StuSex, deptNum = SM.deptNum, AddTime = SM.AddTime }; } } return View(); } }
傳統方法實現相互映射存在一個弊端,如果數據表字段特別多,那么,試問你需要寫多少行代碼?
OK,AutoMapper閃亮登場,那么如果使用AutoMapper需要寫什么樣的代碼呢?

public ActionResult Index() { var profiler = MiniProfiler.Current; using (profiler.Step("查詢Student的數據")) { using (BingFaTestEntities context = new BingFaTestEntities()) { var SM = context.Student.Where(A => A.StuNum == "0813091000000").FirstOrDefault(); if (SM != null) { StudentModel StudentM = Mapper.DynamicMap<StudentModel>(SM); } } return View(); } }
由上述代碼可知,其相互映射只需一行代碼搞定。這里需要注意,你定義的Model層個字段屬性要和DTO層字段屬性一致。
OK,那如果需要轉化一個泛型集合呢?
傳統方法如下:

public ActionResult Index() { var profiler = MiniProfiler.Current; using (profiler.Step("查詢Student的數據")) { using (BingFaTestEntities context = new BingFaTestEntities()) { var SMList = context.Student.Where(A => A.StuName.Contains("陳")).ToList(); if (SMList != null&&SMList.Count>0) { foreach (var SM in SMList) { var StudetM = new StudentModel() { Id = SM.Id, StuName = SM.StuNum, StuNum = SM.StuNum, StuSex = SM.StuSex, deptNum = SM.deptNum, AddTime = SM.AddTime }; } } } return View(); } }
那么,AutoMapper是否可以做到呢?
當然,可以...

public ActionResult Index() { var profiler = MiniProfiler.Current; using (profiler.Step("查詢Student的數據")) { using (BingFaTestEntities context = new BingFaTestEntities()) { var SMList = context.Student.Where(A => A.StuName.Contains("陳")).ToList(); if (SMList != null && SMList.Count > 0) { List<StudentModel> StudentM = Mapper.DynamicMap<List<StudentModel>>(SMList); } } return View(); } }
有上述代碼可知,是不是連Foreach都省了?
哈哈,OK,這些都是些基礎功能,咱們繼續深究。
如果需要映射導航屬性對應表中的字段怎么寫呢?
我們將StudentModel修改成如下:
如果要得到系名稱 deptName ,我們就要用到EF的懶加載。關於用EF懶加載時要注意的事項,大家可參考博客: EF性能優化-有人說EF性能低,我想說:EF確實不如ADO.NET,當然本節也會詳細說明。
首先用傳統方法實現如下:

public ActionResult Index() { var profiler = MiniProfiler.Current; using (profiler.Step("查詢Student的數據")) { using (BingFaTestEntities context = new BingFaTestEntities()) { var SM = context.Student.Where(A => A.StuNum == "0813091000000").FirstOrDefault(); if (SM != null) { var StudetM = new StudentModel() { Id = SM.Id, StuName = SM.StuNum, StuNum = SM.StuNum, StuSex = SM.StuSex, deptNum = SM.deptNum, AddTime = SM.AddTime, deptName=SM.Dept.deptName }; } } return View(); } }
傳統方法變化不大,那么,用AutoMapper怎么實現呢?

public ActionResult Index() { var profiler = MiniProfiler.Current; using (profiler.Step("查詢Student的數據")) { using (BingFaTestEntities context = new BingFaTestEntities()) { var SM = context.Student.Where(A => A.StuNum == "0813091000000").FirstOrDefault(); AutoMapper.Mapper.CreateMap<Student, StudentModel>().ForMember(dest => dest.deptName, opts => opts.MapFrom(src => src.Dept.deptName)); var model = AutoMapper.Mapper.Map<StudentModel>(SM); } return View(); } }
由上述方法可知,使用AutoMapper方法進行映射,需要指定目標字段dest.deptName 以及 源字段 src.Dept.deptName,關於AutoMapper的詳細用法及說明大家可參考:【來龍去脈系列】AutoMapper一款自動映射框架
在這里,我要告誡大家關於使用懶加載的注意事項,如果你不注意,那么你寫的代碼效率有可能將會非常低。
如上述兩種方法,我們來監控下生成的SQL語句:(關於是如果監控生成的SQL語句,大家可參考我的博客:MiniProfiler工具介紹(監控EF生成的SQL語句)--EF,迷你監控器,哈哈哈)
生成了2條SQL語句:
OK,僅僅生成兩條SQL語句還可以接受,但是如果你的項目數據表關系比較復雜,有很多導航屬性時,就會生成很多SQL語句,會產生極大的性能問題。
那么關於懶加載的問題怎么解決呢?還好,EF中有Include,在使用Include時需要引入using System.Data.Entity;
將上邊的程序修改成如下:

public ActionResult Index() { var profiler = MiniProfiler.Current; using (profiler.Step("查詢Student的數據")) { using (BingFaTestEntities context = new BingFaTestEntities()) { var SMList = context.Student.Include(A=>A.Dept).Where(A => A.StuName.Contains("陳")).ToList(); AutoMapper.Mapper.CreateMap<Student, StudentModel>() .ForMember(dest => dest.deptName, opts => opts.MapFrom(src => src.Dept.deptName)); var modelList = AutoMapper.Mapper.Map<List<StudentModel>>(SMList); } return View(); } }
使用Include,其實相當於聲明棄用懶加載,這里使用顯示加載!
OK,關於使用AutoMapper應用懶加載的方法講完了。正如上述所說:AutoMapper是將DTO映射成Model,如果反過來映射是否可行呢?
還好,AutoMapper提供了.ReverseMap();方法,將Model映射成DTO,如下:

public ActionResult Index() { var profiler = MiniProfiler.Current; using (profiler.Step("查詢Student的數據")) { using (BingFaTestEntities context = new BingFaTestEntities()) { StudentModel M = new StudentModel() { StuName = "陳星辰", AddTime = DateTime.Now, deptNum = "0813092", StuNum = "081309201", StuSex = "男" }; Student Sm = new Student(); AutoMapper.Mapper.CreateMap<StudentModel, Student>().ReverseMap(); Sm = AutoMapper.Mapper.Map<Student>(M); context.Student.Add(Sm); context.SaveChanges(); } return View(); } }
OK。截止到這里,關於AutoMapper的基礎用法也就講完了,本人能力有限,如有未提及之處,請大家多多指點。希望大家喜歡!
@陳卧龍的博客