Asp.mvc(二)~使用AutoMapper实现领域模型与DTO映射
2015-07-22 22:33
761 查看
Asp.mvc(二)~使用AutoMapper实现领域模型与DTO映射
上一篇中介绍了Core, Data 以及 Services 层,在介绍 Presentation 层之前,我们需要了解下面几个知识点:AutoMapper
Autofac
以及上篇博文中 Data 层未详细介绍的 WebActivatorEx
AutoMapper
有时候,你需要将一种类型转换为另外一种类型,这种情况在mvc 项目中较为常见,在数据查询的时候,通过数据持久层将数据绑定到领域模型中,在数据写入的时候,通过数据持久层将绑定到领域模型上的数据保存至数据库。 但是在用户界面上, 我们并不需要将数据完完全全的暴露的用户眼前, 所以在这里我们一般都会使用到 “贫血模式”, 建立一个 ViewModel 层,其作用就是将实际需要的数据定义为模型, 也就是DTO (Data Transfer Object), 在用户查询的时候, 程序通常会将数据保存至领域模型中, 然后将领域模型转换至 DTO,来保证传输的数据都是必须的,这样的一种做法可以减少领域模型与 Presentation 的耦合, 以及完全不需要将数据字段暴露在 Presentation 层,也保证了一定情况下数据的安全性,获取必要的数据, 也可以提高数据传输的效率。一般情况下,如果将一个对象的部分属性克隆给另外一个对象, 通常的做法就是:
Object A = new Object(args...); Object B = new Object { Property1 = A.Property1, Property2 = A.Property2, Property3 = A.Property3... };
这样的做法存在着较大的不足:
冗杂,繁琐
灵活性较差
都讨厌编写这种无聊的代码…
我们需要一种工具来帮助我们完成这段枯燥无味的工作: http://automapper.org/
根据官网对AutoMapper的介绍:
AutoMapper is a simple little library built to solve a deceptively complex problem - getting rid of code that mapped one object to another. This type of code is rather dreary and boring to write, so why not invent a tool to do it for us?
可以看出 AutoMapper 就是为了处理这一项枯燥代码的工具。 下面建立一个控制台 Demo 来演示 AutoMapper 的作用:
新建 AutoMapperSample 控制台程序,打开 Nuget 包控制台,键入:
Install-Package AutoMapper
创建以下领域模型:
using System; using System.Collections.Generic; namespace AutoMapperSample { public partial class Student { public string Id { get; set; } public string Name { get; set; } public string Gender { get; set; } public DateTime? Birthday { get; set; } public string ClassId { get; set; } public virtual Class Class { get; set; } } public partial class Class { public Class() { this.Students = new List<Student>(); } public string Id { get; set; } public string Name { get; set; } public List<Student> Students { get; set; } public string GradeId { get; set; } public virtual Grade Grade { get; set; } } public partial class Grade { public Grade() { this.Classes = new List<Class>(); } public string Id { get; set; } public string Name { get; set; } public List<Class> Classes { get; set; } } }
较为清晰的嵌套结构,下面创建 DTO 模型,里面声明一些必要的属性:
using System; namespace AutoMapperSample { public partial class StudentDto { /// <summary> /// 学员编号 --> Student.Id /// </summary> public string Id { get; set; } /// <summary> /// 姓名 --> Student.Name /// </summary> public string Name { get; set; } /// <summary> /// 生日 --> Student.Birthday /// </summary> public DateTime? Birthday { get; set; } /// <summary> /// 班级编号 --> Student.ClassId /// </summary> public string ClassId { get; set; } /// <summary> /// 班级 --> Student.Class.Name /// </summary> public string ClassName { get; set; } /// <summary> /// 年级编号 Student.Class.GradeId /// </summary> public string GradeId { get; set; } /// <summary> /// 年级 Student.Class.Grade.Name /// </summary> public string GradeName { get; set; } } }
在上面的注释中可以看到一些嵌套的映射关系,下面建立映射,创建 Mapping
using AutoMapper; namespace AutoMapperSample { public static class Mapping { /// <summary> /// 注册映射关系 /// </summary> public static void Register() { Mapper.CreateMap<Student, StudentDto>() .ForMember(dest => dest.ClassName, mo => mo.MapFrom(src => src.Class.Name)) .ForMember(dest => dest.GradeId, mo => mo.MapFrom(src => src.Class.GradeId)) .ForMember(dest => dest.GradeName, mo => mo.MapFrom(src => src.Class.Grade.Name)); Mapper.CreateMap<StudentDto, Student>() .ForMember(dest => dest.Gender, mo => mo.Ignore()) .ForMember(dest => dest.Class, mo => mo.Ignore()); } /// <summary> /// 领域模型转化为Dto /// </summary> /// <param name="entity"></param> /// <returns></returns> public static StudentDto ToModel(this Student entity) { return Mapper.Map<Student, StudentDto>(entity); } /// <summary> /// Dto转化为领域模型 /// </summary> /// <param name="model"></param> /// <returns></returns> public static Student ToEntity(this StudentDto model) { return Mapper.Map<StudentDto, Student>(model); } /// <summary> /// 重载 ToEntity, 在已有 Dto模型基础上使用领域模型转换成 Dto /// </summary> /// <param name="model"></param> /// <param name="entity"></param> /// <returns></returns> public static Student ToEntity(this StudentDto model, Student entity) { return Mapper.Map(model, entity); } } }
把解释都写在注释中了,很详细。下面来测试一下:
using System; namespace AutoMapperSample { class Program { static void Main(string[] args) { //注册映射关系 Mapping.Register(); //数据初始化 var grade = new Grade { Id = "g001", Name = "一年级" }; var _class = new Class { Id = "c001", Name = "一班", GradeId = grade.Id, Grade = grade }; var student = new Student { Id = "s001", Name = "Cigarette", Birthday = DateTime.Now, Gender = "Male", ClassId = _class.Id, Class = _class }; _class.Students.Add(student); grade.Classes.Add(_class); //1.Student --> StudentDto var studentDto = student.ToModel(); //2.StudentDto --> Student student = studentDto.ToEntity(); //3.StudentDto --> Student(以一个已存在的Student作为基础) var studentPart = new Student { Gender = "Female", Class = new Class { Name = "new class" } }; student = studentDto.ToEntity(studentPart); Console.ReadKey(); } } }
在1.2.3处分别打上断点,监视情况为: (图片无法上传…所以这里直接Copy了)
1.
- student {AutoMapperSample.Student} AutoMapperSample.Student + Birthday {2015/7/22 21:55:38} System.DateTime? + Class {AutoMapperSample.Class} AutoMapperSample.Class ClassId "c001" string Gender "Male" string Id "s001" string Name "Cigarette" string
2.
- studentDto {AutoMapperSample.StudentDto} AutoMapperSample.StudentDto + Birthday {2015/7/22 21:55:38} System.DateTime? ClassId "c001" string ClassName "一班" string GradeId "g001" string GradeName "一年级" string Id "s001" string Name "Cigarette" string
3.
- student {AutoMapperSample.Student} AutoMapperSample.Student + Birthday {2015/7/22 21:55:38} System.DateTime? - Class {AutoMapperSample.Class} AutoMapperSample.Class Grade null AutoMapperSample.Grade GradeId null string Id null string Name "new class" string + Students Count = 0 System.Collections.Generic.List<AutoMapperSample.Student> ClassId "c001" string Gender "Female" string Id "s001" string Name "Cigarette" string
从上面可以看出我们的领域模型对象student转换为studentDto之后,根据我们所配置的映射关系,数据已经完全映射正确,其实在上面这段代码中,已经解决了两个看似复杂的问题:相同类型不同名的属性映射,嵌套属性映射。
让我们再来看看注册映射规则的重要代码段(Mapping.Register()):
/// <summary> /// 注册映射关系 /// </summary> public static void Register() { Mapper.CreateMap<Student, StudentDto>() .ForMember(dest => dest.ClassName, mo => mo.MapFrom(src => src.Class.Name)) .ForMember(dest => dest.GradeId, mo => mo.MapFrom(src => src.Class.GradeId)) .ForMember(dest => dest.GradeName, mo => mo.MapFrom(src => src.Class.Grade.Name)); Mapper.CreateMap<StudentDto, Student>() .ForMember(dest => dest.Gender, mo => mo.Ignore()) .ForMember(dest => dest.Class, mo => mo.Ignore()); }
通过 Mapper 的静态方法 CreateMap 来创建两个模型之间的映射规则,第一个类型为 Source 即源模型,第二个类型为 Destination 即目标模型,
CreateMap<TSource,TDestination>()
返回一个
IMappingExpression<TSource, TDestination>
,使用它的 ForMember 来定义规则。 前者表示 Student –> StudentDto 的规则,后者则为 StudentDto –> Student 的规则。 这段代码一般都在程序启动后第一时间执行,完成对规则的注册, 在 asp.net mvc项目中, 我们可以使用 WebActivatorEx 来完成这种类型代码的执行,这个是要讲解的第三个点了。 Ok, AutoMapper 的基本使用就已经说得差不多了, 下一篇会了解一下 Autofac 这个依赖注入容器, 以解耦合。
相关文章推荐
- Android开发之SoundPool使用详解
- ios7关于图片资源使用需要注意的问题
- 【攻克Android (6)】事件
- 《慕客网:IOS基础入门之Foundation框架初体验》学习笔记 <五> NSDicionary + NSMutableDictionary
- wxhl ios bj 49 张浩 学习心得连载 第二章
- Android switch当代码setCheck时,不调用onCheckedChanged
- ios7,ios8中关于xcassets资源使用的问题
- IOS学习之蓝牙4.0
- 设计模式在Java/Android中的应用
- Objective-C设计模式——适配器Adapter(接口适配)
- Android入门
- Android之——申请应用系统管理员权限
- Android 之 GridView初探
- iOS之沙盒机制和如何获取沙盒路径
- Objective-C学习笔记(五)——数据类型与限定词
- 如何让你的APK跑在 com.android.phone 进程
- android之适配器
- Android小记:SpannableString
- 对iOS开发中内存管理的一点总结与理解
- 前端新手——适配不同手机端页面的方案,动态查询dpr设置根元素font-size