先說說DTO DTO是個什麼東東? DTO(Data Transfer Object)就是數據傳輸對象,說白了就是一個對象,只不過裡邊全是數據而已。 為什麼要用DTO? 1、DTO更註重數據,對領域對象進行合理封裝,從而不會將領域對象的行為過分暴露給表現層 2、DTO是面向UI的需求而設計的,而領域 ...
先說說DTO
DTO是個什麼東東?
DTO(Data Transfer Object)就是數據傳輸對象,說白了就是一個對象,只不過裡邊全是數據而已。
為什麼要用DTO?
1、DTO更註重數據,對領域對象進行合理封裝,從而不會將領域對象的行為過分暴露給表現層
2、DTO是面向UI的需求而設計的,而領域模型是面向業務而設計的。因此DTO更適合於和表現層的交互,通過DTO我們實現了表現層與領域Model之間的解耦,因此改動領域Model不會影響UI層
3、DTO說白了就是數據而已,不包含任何的業務邏輯,屬於瘦身型的對象,使用時可以根據不同的UI需求進行靈活的運用
AutoMapper
現在我們既然知道了使用DTO的好處,那麼我們肯定也想馬上使用它,但是這裡會牽扯一個問題:怎樣實現DTO和領域Model之間的轉換?
有兩個思路,我們要麼自己寫轉換代碼,要麼使用工具。不過就應用而言,我還是覺得用工具比較簡單快捷,那就使用工具吧。其實這樣的轉換工具很多,不過我還是決定使用AutoMapper,因為它足夠輕量級,而且也非常流行,國外的大牛們都使用它。使用AutoMapper可以很方便的實現DTO和領域Model之間的轉換,它是一個強大的Object-Object Mapping工具。
AutoMapper6.2.2.0
AutoMapper6.2.2.0與之前的版本有些不同,那麼究竟有什麼不同,我們一起來實踐一下:
1.首先,使用AutoMapper6.2.2.0需要在你的項目中引用NuGet包,右鍵依賴項,管理NuGet程式包,然後選擇瀏覽,搜索AutoMapper,安裝,你就可以在項目中使用啦,你也可以在vs中使用打開工具-庫程式包管理器-程式包管理控制平臺,輸入“Install-Package AutoMapper”命令,就可以把AutoMapper添加到項目中了~
2.讓我們開始使用
(1)一個簡單的映射
首先,創建一個C#控制台應用程式,為了方便,我們在Program直接定義三個類:
public class Student { public string Name { get; set; } public int Sex { get; set; } public string Age { get; set; } public DateTime Birth { get; set; } } public class Dto_Student { public string n { get; set; } public string s { get; set; } public int a { get; set; } public string b { get; set; } } public class V_Student { public string Name { get; set; } public int Sex { get; set; } public string Age { get; set; } public DateTime Birth { get; set; } }
(2)一個簡單的映射,由於Student和V_Student類欄位名稱一樣,類型相同,所以,映射可以這麼寫
//一個簡單的映射 AutoMapper.Mapper.Initialize(map => map.CreateMap<Student, V_Student>()); var stu = AutoMapper.Mapper.Map<Student>(new V_Student { Name = "myname", Sex = 1, Age = "24", Birth = DateTime.Now }); var vstu = AutoMapper.Mapper.Map<V_Student>(new Student { Name = "myname", Sex = 1, Age = "24", Birth = DateTime.Now });
(3)那麼,不同欄位名稱,甚至不同類型的兩個類如何映射呢,那就要手動的映射相應欄位了:
//屬性不同名,屬性類型不同映射 AutoMapper.Mapper.Initialize(map => map.CreateMap<Student, Dto_Student>() .ForMember(d => d.n, opt => { opt.MapFrom(s => s.Name); }) .ForMember(d => d.s, opt => { opt.MapFrom(s => s.Sex == 1 ? "男" : "女"); }) .ForMember(d => d.a, opt => { opt.MapFrom(s => Convert.ToInt32(s.Age)); }) .ForMember(d => d.b, opt => { opt.MapFrom(s => s.Birth.ToString("yyyy-MM-dd")); }) ); var dto_stu = AutoMapper.Mapper.Map<Dto_Student>(new Student { Name = "myname", Sex = 1, Age = "24", Birth = DateTime.Now }); var stu = AutoMapper.Mapper.Map<Student>(new Dto_Student { n = "myname", s = "男", a = 24, b = DateTime.Now.ToString("yyyy-MM-dd") });
這邊一運行,發現報錯了,為什麼呢,原來是因為,由於映射欄位類型不同,無法反向映射,那麼,如何再添加一個映射呢,這時候就需要用到Profile這個類,我們需要繼承這個類,併在裡面寫下映射配置,具體如下:
新建Dto_StudentProfile類和StudentProfile類:
public class Dto_StudentProfile:Profile { public Dto_StudentProfile() { base.CreateMap<Dto_Student, Student>() .ForMember(s => s.Name, opt => { opt.MapFrom(stu => stu.n); }) .ForMember(s => s.Sex, opt => { opt.MapFrom(stu => stu.s.Equals("男")?1:0); }) .ForMember(s => s.Age, opt => { opt.MapFrom(stu => stu.a.ToString()); }) .ForMember(s => s.Birth, opt => { opt.MapFrom(stu =>DateTime.Parse( stu.b+" 00:00:00")); }); } } public class StudentProfile:Profile { public StudentProfile() { base.CreateMap<Student, Dto_Student>() .ForMember(d => d.n, opt => { opt.MapFrom(stu => stu.Name); }) .ForMember(d => d.s, opt => { opt.MapFrom(stu => stu.Sex == 1 ? "男" : "女"); }) .ForMember(d => d.a, opt => { opt.MapFrom(stu => Convert.ToInt32(stu.Age)); }) .ForMember(d => d.b, opt => { opt.MapFrom(stu => stu.Birth.ToString("yyyy-MM-dd")); }); } }
那麼,我們如何使用兩個配置呢?
//配置映射 AutoMapper.Mapper.Initialize(map => map.AddProfiles(new[] { typeof(Dto_StudentProfile), typeof(StudentProfile) })); var dto_stu = AutoMapper.Mapper.Map<Dto_Student>(new Student { Name = "myname", Sex = 1, Age = "24", Birth = DateTime.Now }); var stu = AutoMapper.Mapper.Map<Student>(new Dto_Student { n = "myname", s = "男", a = 24, b = DateTime.Now.ToString("yyyy-MM-dd") });
這樣,我們就完成了兩個不同欄位名稱,不同類型的映射
(4)映射List<T>
如何進行實體列表的映射呢,其實,配置上並沒有任何不同,只需要在使用上,換成list就可以了:
List<Student> estu = new List<Student>(); estu.Add(new Student { Name = "myname", Sex = 1, Age = "24", Birth = DateTime.Now }); estu.Add(new Student { Name = "myname", Sex = 1, Age = "24", Birth = DateTime.Now }); List<Dto_Student> slist = AutoMapper.Mapper.Map<List<Student>, List<Dto_Student>>(estu);
(5)你可能覺得不太方便,那麼,我們可以將AutoMapper的Initialize放到應用開始的時候運行
Mvc項目,可以放到程式的Global中,如果是.NET Core 2.0的MVC項目,可以放到StartUp中運行:
這邊直接摘了前人的方法,有興趣可以看一下https://www.cnblogs.com/lvlinlv/p/7344916.html
(6)那麼,你可以使用拓展的方法進行映射,這裡定義一個AutoMapperHelper操作類:
using System; using System.Collections; using System.Collections.Generic; using System.Text; namespace AutoMapperTest { public static class AutoMapperHelper { public static T MapTo<T>(this object obj) { if (obj == null) return default(T); return AutoMapper.Mapper.Map<T>(obj); } public static List<TDestination> MapToList<TDestination>(this object source) { return AutoMapper.Mapper.Map<List<TDestination>>(source); } } }
這樣,你就可以這麼使用:
List<Student> estu = new List<Student>(); estu.Add(new Student { Name = "myname", Sex = 1, Age = "24", Birth = DateTime.Now }); estu.Add(new Student { Name = "myname", Sex = 1, Age = "24", Birth = DateTime.Now }); var slist = estu.MapToList<Dto_Student>();
非list的實體映射也是一樣的,這裡不再多說,如果有什麼疑問,歡迎提出來,共同討論進步,感謝你的閱讀