在我们的业务中经常需要使用到类型之间的映射,特别是在和前端页面进行交互的时候,我们需要定义各种类型的Dto,并且需要需要这些Dto和数据库中的实体进行映射,对于有些大对象而言,需要赋值太多的属性,这样往往会使整个代码不够简洁和明了,有了AutoMapper之后我们就可以通过很少的代码来完成这样一个映射的过程,在了解当前的代码之前,你最好先读一下ABP文档中对于这个部分的介绍,更详细的介绍你可以参考这里

  一 基础篇

  1 注入IObjectMapper接口

  通过接口注入IObjectMapper对象,如果使用ABP框架的话所有继承自ApplicationService的应用服务都可以获取定义于AbpServiceBase中已经注入的公用属性ObjectMapper对象。

  2 定义映射关系

  在当前继承自AbpModule的类下面的Initialize()方法中添加映射关系

  1. public override void Initialize() {
  2. IocManager.RegisterAssemblyByConvention(typeof(DcsApplicationModule).GetAssembly());
  3. IocManager.AddSdtSession<Guid>();
  4.  
  5. Configuration.Modules.AbpAutoMapper().Configurators.Add(config => {
  6. config.CreateMissingTypeMaps = true;
  7.  
  8. #region 客户售后档案,客户售后档案与车辆关系
  9.  
  10. config.CreateMap<CustomerSoldDto, CustomerSold>(MemberList.Source);
  11. config.CreateMap<CustomerVehicleSoldDto, CustomerVehicleSold>(MemberList.Source);
  12. config.CreateMap<CustomerSoldExtendedInput, CustomerSoldExtended>(MemberList.Source)
  13. .ForMember(c => c.C4cmCode, o => o.MapFrom(m => m.CrmCode))
  14. .ForMember(c => c.ReferrerPhone, o => o.MapFrom(m => m.Referrer));
  15. config.CreateMap<CustomerSoldSyncInput, CustomerSold>(MemberList.Source)
  16. .ForMember(c => c.CustomerSoldExtended, o => o.MapFrom(m => m.CustomerSoldExtended))
  17. .ForMember(c => c.JobName, o => o.MapFrom(m => m.JobTitle));
  18.  
  19. #endregion 客户售后档案,客户售后档案与车辆关系
  20. });
  21. }

  上面的代码中我们可以使用ForMember方法来定义自己的映射规则。

  3 使用ObjectMapper映射实体关系

  在有了第一步的工作后,我们就可以在业务代码中调用_objectMapper.Map<T>(input),或者是_objectMapper.Map(input,oldEntity)这种方式来实现从Dto到数据库实体的映射关系。

  二 进阶篇

  1 定义Profile来分散映射关系

  如果我们把所有的映射关系都写在Module的Initialize()方法里面,我们发现对于一个大的项目简直就是一个灾难,因为这个映射关系实在是太多,整个类超级大,现在有一个解决方案就是定义自己的XXXProfile,但是需要继承自基类Profile类。

  1. public class EngineMaintainApplyProfile : Profile {
  2. public EngineMaintainApplyProfile() {
  3. CreateMap<EngineMaintainApply, QueryEngineMaintainApplyOutput>(MemberList.Destination)
  4. .ForMember(d => d.BreakMaintainMark, options => options.MapFrom(s => s.VehicleSold.BreakMaintainMark));
  5.  
  6. CreateMap<EngineMaintainApplyAth, EngineMaintainApplyAthDto>(MemberList.Destination);
  7. CreateMap<EngineMaintainApply, QueryEngineMaintainApplyWithDetailsOutput>(MemberList.Destination)
  8. .ForMember(d => d.Attachments, options => options.MapFrom(s => s.EngineMaintainApplyAths))
  9. .ForMember(d => d.ProductCode, options => options.MapFrom(s => s.VehicleSold.ProductCode))
  10. .ForMember(d => d.EngineCode, options => options.MapFrom(s => s.VehicleSold.EngineCode))
  11. .ForMember(d => d.TransmissionSn, options => options.MapFrom(s => s.VehicleSold.TransmissionSn))
  12. .ForMember(d => d.Color, options => options.MapFrom(s => s.VehicleSold.Color))
  13. .ForMember(d => d.VehiclePurpose, options => options.MapFrom(s => s.VehicleSold.VehiclePurpose))
  14. .ForMember(d => d.InvoiceDate, options => options.MapFrom(s => s.VehicleSold.InvoiceDate))
  15. .ForMember(d => d.SalePrice, options => options.MapFrom(s => s.VehicleSold.SalePrice))
  16. .ForMember(d => d.ProductionDate, options => options.MapFrom(s => s.VehicleSold.ProductionDate))
  17. .ForMember(d => d.VehicleSaleDate, options => options.MapFrom(s => s.VehicleSold.SaleDate));
  18.  
  19. CreateMap<AddEngineMaintainApplyInput, EngineMaintainApply>(MemberList.Source)
  20. .ForMember(d => d.VehicleSoldId, options => options.MapFrom(s => s.VehicleId))
  21. .ForMember(d => d.ExtensionMaintainBeginDate, options => options.MapFrom(s => s.SaleDate))
  22. .ForMember(d => d.EngineMaintainApplyAths, options => options.Ignore());
  23.  
  24. CreateMap<UpdateEngineMaintainApplyInput, EngineMaintainApply>(MemberList.Source)
  25. .ForMember(d => d.RowVersion, options => options.Ignore())
  26. .ForMember(d => d.EngineMaintainApplyAths, options => options.Ignore());
  27. }
  28. }

  使用这个方法的时候需要注意需要在Initialize方法里面添加映射,例如下面的方式。

  1. public override void Initialize() {
  2. IocManager.RegisterAssemblyByConvention(typeof(DcsApplicationModule).GetAssembly());
  3.  
  4. Configuration.Modules.AbpAutoMapper().Configurators.Add(config => {
  5. config.AddMaps(typeof(DcsApplicationModule).GetAssembly());
  6. });
  7. }

  2 添加自动映射关系

  这个需要特别注意,当我们的Dto和目标实体每个字段都能一一对应的情况下,在AutoMapper<8.1.1这个版本的时候能够自动识别并映射,但是当升级到这个版本的时候这个特性会去掉,关于这个内容请点击这里了解详情,AutoMapper的官方暂时给了一个过渡的方案,那就是设置CreateMissingTypeMaps属性设置为true,就像下面的例子。

  1. public override void Initialize() {
  2. IocManager.RegisterAssemblyByConvention(typeof(DcsApplicationModule).GetAssembly());
  3.  
  4. Configuration.Modules.AbpAutoMapper().Configurators.Add(config => {
  5. config.CreateMissingTypeMaps = true;
  6. });
  7. }

  但是这只是一个过渡方案,在这个属性的上面加了一个Obsolete标签,并说明在Version9.0的时候会去掉,所以后面我们也是需要去显式去添加映射关系。

  1. /// <summary>
  2. /// Create missing type maps during mapping, if necessary
  3. /// </summary>
  4. [Obsolete("Support for automatically created maps will be removed in version 9.0. You will need to explicitly configure maps, manually or using reflection. Also consider attribute mapping (http://docs.automapper.org/en/latest/Attribute-mapping.html).")]
  5. bool ? CreateMissingTypeMaps { get; set; }

  3 部分对象的忽略

  有的时候我们将源映射到目标的时候,希望忽略掉一些字段的赋值,比如Id,我们希望框架来自动为我们的实体来添加主键Id,那么我们应该怎么处理呢?

  1. public override void Initialize() {
  2. IocManager.RegisterAssemblyByConvention(typeof(DcsApplicationModule).GetAssembly());
  3.  
  4. Configuration.Modules.AbpAutoMapper().Configurators.Add(config => {
  5. config.CreateMissingTypeMaps = true;
  6. config.CreateMap<VehicleSoldDto, VehicleSold>(MemberList.Source)
  7. .ForMember(d => d.Id, o => o.Ignore());
  8. });
  9. }

  3 ProjectTo方法

  AutoMapper为我们提供了一个IQueryable类型的扩展,能够很方便的让我们进行关系之间的映射,我们来看看具体的源码,然后再看看在具体的项目中是如何使用的?

  1. /// <summary>
  2. /// Extension method to project from a queryable using the provided mapping engine
  3. /// </summary>
  4. /// <remarks>Projections are only calculated once and cached</remarks>
  5. /// <typeparam name="TDestination">Destination type</typeparam>
  6. /// <param name="source">Queryable source</param>
  7. /// <param name="configuration">Mapper configuration</param>
  8. /// <param name="membersToExpand">Explicit members to expand</param>
  9. /// <returns>Expression to project into</returns>
  10. public static IQueryable<TDestination> ProjectTo<TDestination>(
  11. this IQueryable source,
  12. IConfigurationProvider configuration,
  13. params Expression<Func<TDestination, object>>[] membersToExpand
  14. )
  15. => source.ProjectTo(configuration, null, membersToExpand);

  这个快捷方法能够让我们不需要通过ObjectMapper.Map方法来快速进行对象之间的映射,这里需要注意的是这时候这两个对象之间也需要在Profile或者是Initialize方法中通过CreateMap来指定映射关系,这里我们来看看在具体的业务中该如何使用。

  1. /// <summary>
  2. /// 查询维修预约单
  3. /// </summary>
  4. /// <param name="input">查询输入</param>
  5. /// <param name="pageRequest">分页输入</param>
  6. /// <returns>带分页的维修预约单信息</returns>
  7. public async Task<Page<QueryRepairAppointmentsOutput>> QueryRepairAppointmentsAsync(QueryRepairAppointmentsInput input, PageRequest pageRequest) {
  8. var queryResults = _repairAppointmentRepository.GetAll()
  9. .Include(a => a.ServiceAdvisor)
  10. .Where(a => a.DealerId == SdtSession.TenantId) // 经销商Id=登录企业Id
  11. .WhereIf(!string.IsNullOrWhiteSpace(input.Code), a => a.Code.Contains(input.Code))
  12. .WhereIf(!string.IsNullOrWhiteSpace(input.Vin), a => a.Vin.Contains(input.Vin))
  13. .WhereIf(!string.IsNullOrWhiteSpace(input.LicensePlate), a => a.LicensePlate.Contains(input.LicensePlate))
  14. .WhereIf(!string.IsNullOrWhiteSpace(input.CustomerName), a => a.CustomerName.Contains(input.CustomerName))
  15. .WhereIf(!string.IsNullOrWhiteSpace(input.ServiceAdvisorName), a => a.ServiceAdvisor.Name.Contains(input.ServiceAdvisorName))
  16. .WhereIf(input.OrderChannel?.Length > 0, a => input.OrderChannel.Contains(a.OrderChannel))
  17. .WhereIf(input.Status?.Length > 0, a => input.Status.Contains(a.Status))
  18. .WhereIf(input.BeginPlanArriveDate.HasValue, a => input.BeginPlanArriveDate.Value <= a.PlanArriveDate)
  19. .WhereIf(input.EndPlanArriveDate.HasValue, a => a.PlanArriveDate <= input.EndPlanArriveDate)
  20. .WhereIf(input.BeginArriveTime.HasValue, a => input.BeginArriveTime <= a.ArriveTime)
  21. .WhereIf(input.EndArriveTime.HasValue, a => a.ArriveTime <= input.EndArriveTime)
  22. .WhereIf(input.BeginCreateTime.HasValue, a => input.BeginCreateTime <= a.CreateTime)
  23. .WhereIf(input.EndCreateTime.HasValue, a => a.CreateTime <= input.EndCreateTime);
  24. var totalCount = await queryResults.CountAsync();
  25. var pagedResults = await queryResults.ProjectTo<QueryRepairAppointmentsOutput>(_autoMapper.ConfigurationProvider)
  26. .PageAndOrderBy(pageRequest).ToListAsync();
  27. return new Page<QueryRepairAppointmentsOutput>(pageRequest, totalCount, pagedResults);
  28. }

    这里通过一个ProjectTo<T>的方法就能够实现从源到目标的映射,这里需要注意的是这个方法中的_autoMapper是一个IMapper注入的对象,这个参数的注入到底有什么用呢?具体也不是十分清楚,但是在单元测试中需要添加此参数,这个可以去尝试。

  4   AutoMapTo、AutoMapFrom

  简单对象之间的映射关系,可以直接通过给类型打标签的方式来进行,这样就不用自己去通过CreateMap来添加映射了。

  1. [AutoMapTo(typeof(User))]
  2. public class CreateUserInput
  3. {
  4. public string Name { get; set; }
  5.  
  6. public string Surname { get; set; }
  7.  
  8. public string EmailAddress { get; set; }
  9.  
  10. public string Password { get; set; }
  11. }

  最后,点击这里返回整个ABP系列的主目录。

ABP中的AutoMapper的更多相关文章

  1. 说说ABP项目中的AutoMapper,Castle Windsor(痛并快乐着)

    这篇博客要说的东西跟ABP,AutoMapper和Castle Windsor都有关系,而且也是我在项目中遇到的问题,最终解决了,现在的感受就是“痛并快乐着”. 首先,这篇博客不是讲什么新的知识点,而 ...

  2. 在ABP中灵活使用AutoMapper

    demo地址:ABP.WindowsService 该文章是系列文章 基于.NetCore和ABP框架如何让Windows服务执行Quartz定时作业 的其中一篇. AutoMapper简介 Auto ...

  3. ABP源码分析二:ABP中配置的注册和初始化

    一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...

  4. ABP源码分析三十五:ABP中动态WebAPI原理解析

    动态WebAPI应该算是ABP中最Magic的功能之一了吧.开发人员无须定义继承自ApiController的类,只须重用Application Service中的类就可以对外提供WebAPI的功能, ...

  5. ABP源码分析四十七:ABP中的异常处理

    ABP 中异常处理的思路是很清晰的.一共五种类型的异常类. AbpInitializationException用于封装ABP初始化过程中出现的异常,只要抛出AbpInitializationExce ...

  6. ABP中使用Redis Cache(1)

    本文将讲解如何在ABP中使用Redis Cache以及使用过程中遇到的各种问题.下面就直接讲解使用步骤,Redis环境的搭建请直接网上搜索. 使用步骤: 一.ABP环境搭建 到http://www.a ...

  7. ABP中使用Redis Cache(2)

    上一篇讲解了如何在ABP中使用Redis Cache,虽然能够正常的访问Redis,但是Redis里的信息无法同步更新.本文将讲解如何实现Redis Cache与实体同步更新.要实现数据的同步更新,我 ...

  8. ABP中使用OAuth2(Resource Owner Password Credentials Grant模式)

    ABP目前的认证方式有两种,一种是基于Cookie的登录认证,一种是基于token的登录认证.使用Cookie的认证方式一般在PC端用得比较多,使用token的认证方式一般在移动端用得比较多.ABP自 ...

  9. 在Abp中集成Swagger UI功能

    在Abp中集成Swagger UI功能 1.安装Swashbuckle.Core包 通过NuGet将Swashbuckle.Core包安装到WebApi项目(或Web项目)中. 2.为WebApi方法 ...

随机推荐

  1. [SDOI2009][BZOJ 1876]SuperGCD

    Description Sheng bill有着惊人的心算能力,甚至能用大脑计算出两个巨大的数的GCD(最大公约 数)!因此他经常和别人比 赛计算GCD.有一天Sheng bill很嚣张地找到了你,并 ...

  2. 微信小程序 图片设置为圆形

    要图片圆形显示,需要设置border-radius:50%,还要设置overflow:hidden,具体如下: Tip:user-avatar是图片控件的class .user-avatar { wi ...

  3. 深度学习面试题10:二维卷积(Full卷积、Same卷积、Valid卷积、带深度的二维卷积)

    目录 二维Full卷积 二维Same卷积 二维Valid卷积 三种卷积类型的关系 具备深度的二维卷积 具备深度的张量与多个卷积核的卷积 参考资料 二维卷积的原理和一维卷积类似,也有full卷积.sam ...

  4. VC++ 返回13位时间戳(Unix时间戳)

    //获取13位时间戳 CString GetUnixTime() { CString nowTime; SYSTEMTIME sysTime; GetLocalTime(&sysTime); ...

  5. CMU Database Systems - Sorting,Aggregation,Join

    Sorting 排序如果可在内存里面排,用经典的排序算法就ok,比如快排 问题在于,数据表中的的数据是很多的,没法一下都放到内存里面进行排序 所以就需要用到,外排,多路并归排序 看下最简单的,2路并归 ...

  6. C++模板编程中只特化模板类的一个成员函数(花样特化一个成员函数)

    转自:https://www.cnblogs.com/zhoug2020/p/6581477.html 模板编程中如果要特化或偏特化(局部特化)一个类模板,需要特化该类模板的所有成员函数.类模板中大多 ...

  7. 廖雪峰Git教程2

    转自:https://www.liaoxuefeng.com/wiki/896043488029600 [远程仓库] 到目前为止,我们已经掌握了如何在Git仓库里对一个文件进行时光穿梭,你再也不用担心 ...

  8. PHP 循环输出多重数组元素

    <?php $arr = array( array( array( array( array( ,, ) ) ), array( ,,) ) ), array(, , ) ); function ...

  9. Typescript 介绍和安装编译

    一. Typescript 介绍 1. TypeScript 是由微软开发的一款开源的编程语言. 2. TypeScript 是 Javascript 的超级,遵循最新的 ES6.Es5 规范.Typ ...

  10. org/apache/curator/RetryPolicy at com.alibaba.dubbo.remoting.zookeeper.curator.CuratorZookeeperTransporter.connect(CuratorZookeeperTransporter.java:26)

    使用dubbo服务,启动项目报错: org/apache/curator/RetryPolicy at com.alibaba.dubbo.remoting.zookeeper.curator.Cur ...