首页 > 学院 > 开发设计 > 正文

【AutoMapper官方文档】DTO与Domin Model相互转换(上)

2019-11-17 03:11:45
字体:
来源:转载
供稿:网友

【AutoMapper官方文档】DTO与Domin Model相互转换(上)

写在前面

  AutoMapper目录:

  • 【AutoMapper官方文档】DTO与Domin Model相互转换(上)
  • 【AutoMapper官方文档】DTO与Domin Model相互转换(中)
  • 【AutoMapper官方文档】DTO与Domin Model相互转换(下)
  • 未完待续。。。

  本篇目录:

  • Flattening-复杂到简单

  • PRojection-简单到复杂

  • Configuration Validation-配置验证

  • Lists and Array-集合和数组

  • Nested mappings-嵌套映射

  • 后记

  上一篇《【道德经】漫谈实体、对象、DTO及AutoMapper的使用》,因为内容写的有点跑偏,关于AutoMapper的使用最后只是简单写了下,很明显这种简单的使用方式不能满足项目中复杂的需要,网上找了下AutoMapper相关文档,但差不多都是像我一样简单的概述下,看来懒的不只有我一个,哈哈。在AutoMapper 官方文档中找到其使用的详细说明,天书一样的英文,然后就找相关中文文档,最后还是没找到,这边没办法,只能自己动手,丰衣足食了。英语牛逼的可以直接略过,查看英文文档,本篇也不算是翻译,因为本人英语实在拿不出手,只是按照示例加上自己的一些理解,做个学习笔记,不对的地方还请指正。

  注:虽然上一篇写跑偏了,但是本人真的很喜欢道德经,除了为人处世,在软件设计这方面其实也有体现,也希望可以运用到这上面,如果你和我有一样的想法,请在点击公告栏中的QQ链接,知音难觅啊!

Flattening-复杂到简单

  Flattening 翻译为压扁、拉平、扁平化的意思,可以理解为使原有复杂的结构变得简化,我们先看下领域模型和DTO代码:

 1     public class Order 2     { 3         private readonly IList<OrderLineItem> _orderLineItems = new List<OrderLineItem>(); 4         public Customer Customer { get; set; } 5         public OrderLineItem[] GetOrderLineItems() 6         { 7             return _orderLineItems.ToArray(); 8         } 9         public void AddOrderLineItem(Product product, int quantity)10         {11             _orderLineItems.Add(new OrderLineItem(product, quantity));12         }13         public decimal GetTotal()14         {15             return _orderLineItems.Sum(li => li.GetTotal());16         }17     }18 19     public class Product20     {21         public decimal Price { get; set; }22         public string Name { get; set; }23     }24 25     public class OrderLineItem26     {27         public OrderLineItem(Product product, int quantity)28         {29             Product = product;30             Quantity = quantity;31         }32         public Product Product { get; private set; }33         public int Quantity { get; private set; }34         public decimal GetTotal()35         {36             return Quantity * Product.Price;37         }38     }39 40     public class Customer41     {42         public string Name { get; set; }43     }44 45     public class OrderDto46     {47         public string CustomerName { get; set; }48         public decimal Total { get; set; }49     }

  可以看到领域模型 Order 是很复杂的,但是对于业务场景中的OrderDto却很简单,只有 CustomerName和Total两个属性,AutoMapper配置代码:

 1         public void Example() 2         { 3             var customer = new Customer 4             { 5                 Name = "George Costanza" 6             }; 7             var order = new Order 8             { 9                 Customer = customer10             };11             var bosco = new Product12             {13                 Name = "Bosco",14                 Price = 4.99m15             };16             order.AddOrderLineItem(bosco, 15);17             // 配置 AutoMapper18             Mapper.CreateMap<Order, OrderDto>();19             // 执行 mapping20             OrderDto dto = Mapper.Map<Order, OrderDto>(order);21             Console.WriteLine("CustomerName:" + dto.CustomerName);22             Console.WriteLine("Total:" + dto.Total);23         }

  转换效果:

  可以看到配置相当的简单,只要设置下Order和OrderDto之间的类型映射就可以了,我们看OrderDto中的CustomerName和Total属性在领域模型Order中并没有与之相对性,没什么可以转换呢,感觉好神奇的样子,其实仔细发现这些属性的命名都有一定的规则,AutoMapper在做解析的时候会按照PascalCase(帕斯卡命名法),就是一种变量命名法,除了PascalCase还有Hungarian(匈牙利命名法)和camelCase(骆驼命名法),PascalCase就是指混合使用大小写字母来构成变量和函数的名字,首字母要大写,camelCase首字母小写,我们C#命名中,一般使用的是camelCase和PascalCase,比较高级的是PascalCase。

  但是为什么AutoMapper会解析Total呢?因为在领域模型Order中有个GetTotal()方法,AutoMapper会解析“Get”之后的单词,所以会与Total相对应,如果你把OrderDto的属性“Total”改为“Totals”,就会发现得到的“Totals”为0。理解了AutoMapper的解析方式,我们就要注意在编写变量、属性或是方法名称的时候一定要规范,这也是一种好的习惯。

Projection-简单到复杂

  Projection 翻译为投影,Flattening是由复杂结构简化,Projection正好相反,投影可以理解为由原始结构千变万化,我们看下两种转换结构:

 1     public class CalendarEvent 2     { 3         public DateTime EventDate { get; set; } 4         public string Title { get; set; } 5     } 6  7     public class CalendarEventForm 8     { 9         public DateTime EventDate { get; set; }10         public int EventHour { get; set; }11         public int EventMinute { get; set; }12         public string Title { get; set; }13     }

  CalendarEvent是原始结构,CalendarEventForm是我们需要转换后的结构,可以看到CalendarEventForm要比CalendarEvent结构复杂些,看下AutoMapper配置转换代码:

 1         public void Example() 2         { 3             var calendarEvent = new CalendarEvent 4             { 5                 EventDate = new DateTime(2008, 12, 15, 20, 30, 0), 6                 Title = "Company Holiday Party" 7             }; 8  9             // 配置 AutoMapper10             Mapper.CreateMap<CalendarEvent, CalendarEventForm>()11                 .ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.EventDate.Date))//定义映射规则12                 .ForMember(dest => dest.EventHour, opt => opt.MapFrom(src => src.EventDate.Hour))//定义映射规则13                 .ForMember(dest => dest.EventMinute, opt => opt.MapFrom(src => src.EventDate.Minute));//定义映射规则14 15             // 执行 mapping16             CalendarEventForm form = Mapper.Map<CalendarEvent, CalendarEventForm>(calendarEvent);17 18             Console.WriteLine("EventDate:"+form.EventDate);19             Console.WriteLine("EventHour:" + form.EventHour);20             Console.WriteLine("EventMinute:" + form.EventMinute);21             Console.WriteLine("Title:" + form.Title);22         }

  和Flattening不同的是,我们除了定义类型映射,还要自定义映射规则,src.EventDate.Date指向dest.EventDate,src.EventDate.Minute指向dest.EventMinute,src.EventDate.Hour指向dest.EventHour,当然我们还可以在MapFrom方法中做一些复杂的映射关系操作,MapFrom接受一个lambda表达式作为参数,可以是任何的Func表达式。Projection适用于由简单到复杂的结构映射,一般体现在业务场景很复杂的情况下。

  【更正:Projection也不一定适用在由简单到复杂的场景,应该说使用Projection就是把AutoMapper的映射配置交给用户来操作】

Configuration Validation-配置验证

  我们在使用Flattening的前提是我们需要转换的结构命名是没有错误的,但是如果我们没有使用PascalCase命名法,或者说我们命名是错误的,该怎么办呢?比如下面代码:

1         public class Source2         {3             public int SomeValue { get; set; }4         }5 6         public class Destination7         {8             public int SomeValuefff { get; set; }9         }

  可以看到Source和Destination中的字段并不相对应,我们测试下AutoMapper映射:

  AssertConfigurationIsValid方法是验证结构映射的,如果配置不正确,会报“AutoMapperConfigurationException”异常错误,如何解决这个问题?你可能会说,就不能改下SomeValuefff的名称吗?这种方法可以,但是如果业务场景中必须要使用怎么办呢,看了上面Projection的映射配置,你可能想到解决方法了,如下:

1             Mapper.CreateMap<Source, Destination>()2                 .ForMember(dest => dest.SomeValuefff, opt => opt.MapFrom(src => src.SomeValue));

  名称不对,我们可以自定义映射规则,虽然这种方式可以,但是如果业务场景中SomeValuefff并不需要,那我们改怎么办?既然有问题,就有解决之道,AutoMapper提供了Ignore方法,忽略不需要映射的数据结构,我们这样配置就可以了:

1             Mapper.CreateMap<Source, Destination>()2                 .ForMember(dest => dest.SomeValuefff, opt => opt.Ignore());

Lists and Array-集合和数组

  有时候我们除了类型映射之外,还需要对集合类型进行映射,先看个示例:

 1             public void Example() 2             { 3                 var sources = new[] 4                     { 5                         new Source {Value = 5}, 6                         new Source {Value = 6}, 7                         new Source {Value = 7} 8                     }; 9                 //配置AutoMapper10                 Mapper.Initialize(cfg =>11                 {12                     cfg.CreateMap<Source, Destination>();13                 });14                 //配置和执行映射15                 IEnumerable<Destination> ienumerableDest = Mapper.Map<Source[], IEnumerable<Destination>>(sources);16                 ICollection<Destination> icollectionDest = Mapper.Map<Source[], ICollection<Destination>>(sources);17                 ILis
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表