先说说 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 工具.
摘自让 AutoMapper 在你的项目里飞一会儿
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 中运行
(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 的实体映射也是一样的, 这里不再多说, 如果有什么疑问, 欢迎提出来, 共同讨论进步, 感谢你的阅读
来源: https://www.cnblogs.com/CoderLinkf/p/8881133.html