Dto数据传输对象
数据传输对象(DTO)(Data Transfer Object),是一种设计模式之间传输数据的软件应用系统。数据传输目标往往是数据访问对象从数据库中检索数据。数据传输对象与数据交互对象或数据访问对象之间的差异是一个以不具有任何行为除了存储和检索的数据(访问和存取器)。
为什么要使用对象传输模型?
- 可以跨平台、存在多平台的字符类型不一致
- 数据安全容易解耦
- 领域模型带有业务
- 效率更好
Dto和视图模型(ViewModel)有什么区别:
仔细思考
- 在数据库存储性别字段Sex中,0代表男,1代表女,我们可以在后台渲染前端直接显示男女,两个都可以实现没有什么区别
- 但是如果你有PC端,移动端,小程序,让它显示英文man、先生、女士等等,显示其他含义,这个时候Dto就支持个性化了。
说完这些你可能还是不懂Dto到底是干什么用的
多种模型概论
- BO-BO(business object) 业务对象-主要作用是把业务逻辑封装为一个对象。这个对象可以包括一个或多个其它的对象
- DAO-DAO(data access object) 数据访问对象(直接访问数据库)
- DO-DO(Domain Object)领域对象
- DTO-DTO(Data Transfer Object)数据传输对象
- PO-PO(persistant object) 持久对象-从db拿出来的展示数据,没有操作
- POCO-POCO(Plain Old CLR Object) 简单无规则 CLR 对象(POCO是DO/DTO/BO/VO的统称)
- VO-VO(View Object) 视图对象-用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来,一个对象对应一个页面等等。
如何使用Dto
public class GoodsEntity//这是我们的实体类 { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } public DateTime? CreateTime { get; set; } public bool IsDeleted { get; set; } public List<Items> Items { get; set; } public BrandsEntity Brands { get; set; } }
先按照实体类定义一个Dto模型
public class GoodsDto { public string GoodsName { get; set; } public decimal Price { get; set; } public string CreateTime { get; set; } public string Content { get; set; } public bool IsDeleted { get; set; } public List<Items> Items { get; set; } public string BrandName { get; set; } }
因为我们页面上不需要展示id所以我去掉了,还有页面的属性名称不一样,但是Dto都包括在实体里面。使用automapper 映射:
- 首先安装依赖包AutoMapper.Extensions.Microsoft.DependencyInjection
- 然后在.net Core ConfigureServices中注入服务
services.AddAutoMapper(typeof(Startup));
- 最后定义一个AutoMapperConfigs.cs的类,该类提供AutoMapper规则配置的入口,它只提供一个静态的方法,在程序第一次运行的时候调用该方法完成配置。
public class AutoMapperConfigs { public static MapperConfiguration RegisterMappings() { return new MapperConfiguration(cfg => { cfg.AddProfile(new GoodsProfile()); }); } }
都完成好了那该怎么样使用呢
GoodsProfile类继承了AutoMapper的Profile类
public class GoodsProfile : Profile { // 添加你的实体映射关系. public GoodsProfile() { // GoodsEntity转GoodsDto. CreateMap<GoodsEntity, GoodsDto>() // 映射发生之前 // 映射之前统一处理 .BeforeMap((src, dest) => src.Price = src.Price + 10) // 默认赋值 .BeforeMap((src, dest) => src.CreateTime = src.CreateTime == null ? (new DateTime(2012, 12, 12)) : src.CreateTime) // 映射匹配 .ForMember(dest => dest.GoodsName, opt => opt.MapFrom(src => src.Name)) .ForMember(dest => dest.CreateTime, opt => opt.MapFrom(src => ((DateTime)src.CreateTime).ToString("yyyy-MM-dd"))) // 匹配的过程中赋值 .ForMember(dest => dest.Price, opt => opt.MapFrom(src => src.Price + 10)) // 忽略某个属性的映射 .ForMember(dest => dest.IsDeleted, opt => opt.Ignore()) // 合并 .ForMember(dest => dest.GoodsName, opt => opt.MapFrom(src => src.Brands.Name +" "+ src.Name)) // 映射发生之后 .AfterMap((src, dest) => dest.GoodsName = dest.Price < 40 ? "N/A." : dest.GoodsName) .AfterMap((src, dest) => dest.Content = "数据传输对象(DTO)(DataTransfer Object)"); // 最简单的匹配,属性字段/类型等完全一致 // GoodsDto转GoodsEntity. CreateMap<GoodsDto, GoodsEntity>(); } }
创建规则:
1.事前事后规则
我们获取到数据以后,时间为空,但是又不能展示到前端,这个时候在映射到前端之前对时间赋默认值。我们在获取金额以后,需要在打折或者加减
事前BeforeMap,我们操作的是源数据src=GoodsEntity
事后AfterMap,我们操作的是dest=GoodsDto
2.默认赋值ForMember
3.属性不一致匹配,我们的name和GoodsName不一致
4.忽略匹配,我们可以不要那些属性
5.实体面没有的值在Dto之后也可以在页面显示
实体的值
最后映射的值
根据上面的规则把价格低于40的商品名字改为了N/A
把商品价格加了20
在Dto中新增了实体中没有的属性Content并赋值
把为空的日期赋值为2012-12-12
把name的值映射到GoodsName上
把Name和Brands.Name两个字段合并值
把isDeleted的值忽略掉全变成false
当有多个Profile的时候,我们可以这样添加:
public class Configuration { public static void Configure() { Mapper.Initialize(cfg => { cfg.AddProfile<Profiles.SourceProfile>(); cfg.AddProfile<Profiles.OrderProfile>(); cfg.AddProfile<Profiles.CalendarEventProfile>(); }); } }