DTO与实体对象的区别
1、什么是DTO
DTO(Data Tansfer Object)即数据传输对象。之前不明白有些框架中为什么要专门定义DTO来绑定表现层中的数据,为什么不能直接用实体模型呢,有了DTO同时还要维护DTO与Model之间的映射关系,多麻烦。
然后看了这篇文章中的讨论部分才恍然大悟。
表现层与应用层之间是通过数据传输对象(DTO)进行交互的,数据传输对象是没有行为的POCO对象,它 的目的只是为了对领域对象进行数据封装,实现层与层之间的数据传递。为何不能直接将领域对象用于 数据传递?因为领域对象更注重领域,而DTO更注重数据。不仅如此,由于“富领域模型”的特点,这样 做会直接将领域对象的行为暴露给表现层。
需要了解的是,数据传输对象DTO本身并不是业务对象。数据传输对象是根据UI的需求进行设计的,而不 是根据领域对象进行设计的。比如,Customer领域对象可能会包含一些诸如FirstName, LastName, Email, Address等信息。但如果UI上不打算显示Address的信息,那么CustomerDTO中也无需包含这个 Address的数据
**简单来说Model面向业务,我们是通过业务来定义Model的。而DTO是面向界面UI,是通过UI的需求来定义的。**通过DTO我们实现了表现层与Model之间的解耦,表现层不引用Model,如果开发过程中我们的模型改变了,而界面没变,我们就只需要改Model而不需要去改表现层中的东西。
还可参考
https://www.cnblogs.com/flyingeagle/articles/7658786.html
https://www.cnblogs.com/xiadao521/p/4379682.html
AutoMapper
官网:http://automapper.org/
源码:https://github.com/AutoMapper/AutoMapper
这是一个轻量级的解决对象间映射问题的框架,并且AutoMapper允许我们根据自己的实际需求进行映射配置,使用起来较灵活。
一,使用AutoMapper进行对象间映射https://cloud.tencent.com/developer/article/1328660
在开发过程中,难免遇到下面这种情况:两个(或多个)对象所拥有的大多数属性是重复的,我们需要在对象间进行映射(即将一个对象的属性值赋给另一个对象。通常我们可以进行如下操作:
1 A a=new A();
2 a.Name=b.Name;
3 a.Age=b.Age;
但若对象拥有较多属性,采用着用方法将会显得十分繁琐。那么有没有一些框架可以帮助我们完成这个过程呢?答案是肯定的。
这里小编使用的是AutoMapper框架,这是一个轻量级的解决对象间映射问题的框架,并且AutoMapper允许我们根据自己的实际需求进行映射配置,使用起来较灵活。
- 一对一映射
首先使用NuGet添加对AutoMapper的引用,然后创建两个类Human和Monkey
class Human
{
public string Name { set; get; }
public int Age { set; get; }
public string Country { set; get; }
}
class Monkey
{
public string Name { set; get; }
public int Age { set; get; }
}
现在我们进行Huamn实例和Monkey实例间的映射:
Monkey monkey = new Monkey() { Name = "monkey", Age = 100 };
//使用AutoMapper时要先进行初始化
Mapper.Initialize(cfg => cfg.CreateMap<Monkey, Human>()
//我们可以根据实际需要来进行初始化,Monkey类没有Country属性
//这里我们给Human对象的Country属性指定一个值
.ForMember(h => h.Country, mce => mce.UseValue("china"))
);
Human human = Mapper.Map<Human>(monkey);
Console.WriteLine("姓名:{0},国籍:{1}", human.Name, human.Country);
程序运行结果:
可以看到,我们已经成功的将monkey对象的属性值映射到了human上。
2. 多对多映射
向对于一对一的映射而言,多对多的映射略显复杂。这里通过一个自定义类来封装具体的映射过程,代码如下:
static class EntityMapper
{
public static List<TDestination> Mapper<TDestination>(object[] source) where TDestination : class
{
if (source != null && source.Length > 0)
{
List<TDestination> results = new List<TDestination>();
foreach (var s in source)
{
results.Add(Mapper<TDestination>(s));
}
if (results.Count > 0)
{
return results;
}
}
return null;
}
public static TDestination Mapper<TDestination>(object source) where TDestination : class
{
Type sourceType = source.GetType();
Type destinationType = typeof(TDestination);
AutoMapper.Mapper.Initialize(cfg => cfg.CreateMap(sourceType, destinationType));
var result = AutoMapper.Mapper.Map(source, sourceType, destinationType);
return (TDestination)result;
}
}
执行映射:
1 Monkey[] monkeys =
2 {
3 new Monkey() {Name="monkey1",Age=1 },
4 new Monkey() {Name="monkey2",Age=2 },
5 new Monkey() {Name="monkey3",Age=3 },
6 new Monkey() {Name="monkey4",Age=4 }
7 };
8
9 List<Human> humans = EntityMapper.Mapper<Human>(monkeys);
10
11 foreach (var h in humans)
12 {
13 Console.WriteLine(h.Name);
14 }
程序运行结果:
这里虽然成功实现了映射,但无法给某个具体的human对象的Country属性赋值,若读者有更好的实现多对多映射的方式,望告知小编。
3. 多对一映射
1 Monkey monkey1 = new Monkey() { Name = "monkey1" };
2 Mapper.Initialize(cof => cof.CreateMap<Monkey, Human>());
3 Human human = Mapper.Map<Human>(monkey1);
4
5 Monkey monkey2 = new Monkey() { Age = 100 };
6 Mapper.Initialize(cof => cof.CreateMap<Monkey, Human>()
7 .ForMember(h => h.Name, mc => mc.UseValue(human.Name))
8 );
9 human = Mapper.Map<Human>(monkey2);
10
11 Console.WriteLine("姓名:{0},年龄:{1}", human.Name, human.Age);
程序运行结果:
二,再看一个问题
AutoMapper 怎么映射多个实体中部分数据到一个DTO?https://stackoverflow.com/questions/21413273/automapper-convert-from-multiple-sources
Let's say I have two model classes:
public class People {
public string FirstName {get;set;}
public string LastName {get;set;}
}
Also have a class Phone:
public class Phone {
public string Number {get;set;}
}
And I want to convert to a PeoplePhoneDto like this:
public class PeoplePhoneDto {
public string FirstName {get;set;}
public string LastName {get;set;}
public string PhoneNumber {get;set;}
}
Let's say in my controller I have:
var people = repository.GetPeople(1);
var phone = repository.GetPhone(4);
// normally, without automapper I would made
return new PeoplePhoneDto(people, phone) ;
I cannot seem to find any example for this scenario. Is this possible ?
Note: Example is not-real, just for this question.
You cannot directly map many sources to single destination - you should apply maps one by one, as described in Andrew Whitaker answer. So, you have to define all mappings:
Mapper.CreateMap<People, PeoplePhoneDto>();
Mapper.CreateMap<Phone, PeoplePhoneDto>()
.ForMember(d => d.PhoneNumber, a => a.MapFrom(s => s.Number));
Then create destination object by any of these mappings, and apply other mappings to created object. And this step can be simplified with very simple extension method:
public static TDestination Map<TSource, TDestination>(
this TDestination destination, TSource source)
{
return Mapper.Map(source, destination);
}
Usage is very simple:
var dto = Mapper.Map<PeoplePhoneDto>(people)
.Map(phone);
三,8分钟学会使用AutoMapper
https://www.cnblogs.com/ZaraNet/p/10000311.html
另外参考链接有
具体见https://www.cnblogs.com/kid1412/p/6003520.html
ABP理论学习之数据传输对象(DTO)
AutoMapper完成Dto与Model的转换
AutoMapper用法