您的位置:首页 > 移动开发

Asp.mvc(二)~使用AutoMapper实现领域模型与DTO映射

2015-07-22 22:33 676 查看

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 这个依赖注入容器, 以解耦合。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: