(重要)实体与DTO以及AutoMapper框架学习

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允许我们根据自己的实际需求进行映射配置,使用起来较灵活。

  1. 一对一映射
    首先使用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用法

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值