• ABP中的AutoMapper


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

      一 基础篇

      1 注入IObjectMapper接口

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

      2 定义映射关系

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

    public override void Initialize() {
                IocManager.RegisterAssemblyByConvention(typeof(DcsApplicationModule).GetAssembly());
                IocManager.AddSdtSession<Guid>();
    
                Configuration.Modules.AbpAutoMapper().Configurators.Add(config => {
                    config.CreateMissingTypeMaps = true;
                    
                    #region 客户售后档案,客户售后档案与车辆关系
    
                    config.CreateMap<CustomerSoldDto, CustomerSold>(MemberList.Source);
                    config.CreateMap<CustomerVehicleSoldDto, CustomerVehicleSold>(MemberList.Source);
                    config.CreateMap<CustomerSoldExtendedInput, CustomerSoldExtended>(MemberList.Source)
                        .ForMember(c => c.C4cmCode, o => o.MapFrom(m => m.CrmCode))
                        .ForMember(c => c.ReferrerPhone, o => o.MapFrom(m => m.Referrer));
                    config.CreateMap<CustomerSoldSyncInput, CustomerSold>(MemberList.Source)
                        .ForMember(c => c.CustomerSoldExtended, o => o.MapFrom(m => m.CustomerSoldExtended))
                        .ForMember(c => c.JobName, o => o.MapFrom(m => m.JobTitle));
    
                    #endregion 客户售后档案,客户售后档案与车辆关系               
                });
            }
    

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

      3 使用ObjectMapper映射实体关系

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

      二 进阶篇

      1 定义Profile来分散映射关系

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

    public class EngineMaintainApplyProfile : Profile {
            public EngineMaintainApplyProfile() {
                CreateMap<EngineMaintainApply, QueryEngineMaintainApplyOutput>(MemberList.Destination)
                    .ForMember(d => d.BreakMaintainMark, options => options.MapFrom(s => s.VehicleSold.BreakMaintainMark));
    
                CreateMap<EngineMaintainApplyAth, EngineMaintainApplyAthDto>(MemberList.Destination);
                CreateMap<EngineMaintainApply, QueryEngineMaintainApplyWithDetailsOutput>(MemberList.Destination)
                    .ForMember(d => d.Attachments, options => options.MapFrom(s => s.EngineMaintainApplyAths))
                    .ForMember(d => d.ProductCode, options => options.MapFrom(s => s.VehicleSold.ProductCode))
                    .ForMember(d => d.EngineCode, options => options.MapFrom(s => s.VehicleSold.EngineCode))
                    .ForMember(d => d.TransmissionSn, options => options.MapFrom(s => s.VehicleSold.TransmissionSn))
                    .ForMember(d => d.Color, options => options.MapFrom(s => s.VehicleSold.Color))
                    .ForMember(d => d.VehiclePurpose, options => options.MapFrom(s => s.VehicleSold.VehiclePurpose))
                    .ForMember(d => d.InvoiceDate, options => options.MapFrom(s => s.VehicleSold.InvoiceDate))
                    .ForMember(d => d.SalePrice, options => options.MapFrom(s => s.VehicleSold.SalePrice))
                    .ForMember(d => d.ProductionDate, options => options.MapFrom(s => s.VehicleSold.ProductionDate))
                    .ForMember(d => d.VehicleSaleDate, options => options.MapFrom(s => s.VehicleSold.SaleDate));
    
                CreateMap<AddEngineMaintainApplyInput, EngineMaintainApply>(MemberList.Source)
                    .ForMember(d => d.VehicleSoldId, options => options.MapFrom(s => s.VehicleId))
                    .ForMember(d => d.ExtensionMaintainBeginDate, options => options.MapFrom(s => s.SaleDate))
                    .ForMember(d => d.EngineMaintainApplyAths, options => options.Ignore());
    
                CreateMap<UpdateEngineMaintainApplyInput, EngineMaintainApply>(MemberList.Source)
                    .ForMember(d => d.RowVersion, options => options.Ignore())
                    .ForMember(d => d.EngineMaintainApplyAths, options => options.Ignore());
            }
        }
    

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

    public override void Initialize() {
                IocManager.RegisterAssemblyByConvention(typeof(DcsApplicationModule).GetAssembly());           
    
                Configuration.Modules.AbpAutoMapper().Configurators.Add(config => {                
                   config.AddMaps(typeof(DcsApplicationModule).GetAssembly());                             
                });
            }
    

      2 添加自动映射关系

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

    public override void Initialize() {
                IocManager.RegisterAssemblyByConvention(typeof(DcsApplicationModule).GetAssembly());            
    
                Configuration.Modules.AbpAutoMapper().Configurators.Add(config => {                
                    config.CreateMissingTypeMaps = true;
                });
            }
    

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

     /// <summary>
            /// Create missing type maps during mapping, if necessary
            /// </summary>
            [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).")]
            bool ? CreateMissingTypeMaps { get; set; }
    

      3 部分对象的忽略

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

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

      3 ProjectTo方法

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

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

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

     /// <summary>
            /// 查询维修预约单
            /// </summary>
            /// <param name="input">查询输入</param>
            /// <param name="pageRequest">分页输入</param>
            /// <returns>带分页的维修预约单信息</returns>
            public async Task<Page<QueryRepairAppointmentsOutput>> QueryRepairAppointmentsAsync(QueryRepairAppointmentsInput input, PageRequest pageRequest) {
                var queryResults = _repairAppointmentRepository.GetAll()
                    .Include(a => a.ServiceAdvisor)
                    .Where(a => a.DealerId == SdtSession.TenantId) //	经销商Id=登录企业Id
                    .WhereIf(!string.IsNullOrWhiteSpace(input.Code), a => a.Code.Contains(input.Code))
                    .WhereIf(!string.IsNullOrWhiteSpace(input.Vin), a => a.Vin.Contains(input.Vin))
                    .WhereIf(!string.IsNullOrWhiteSpace(input.LicensePlate), a => a.LicensePlate.Contains(input.LicensePlate))
                    .WhereIf(!string.IsNullOrWhiteSpace(input.CustomerName), a => a.CustomerName.Contains(input.CustomerName))
                    .WhereIf(!string.IsNullOrWhiteSpace(input.ServiceAdvisorName), a => a.ServiceAdvisor.Name.Contains(input.ServiceAdvisorName))
                    .WhereIf(input.OrderChannel?.Length > 0, a => input.OrderChannel.Contains(a.OrderChannel))
                    .WhereIf(input.Status?.Length > 0, a => input.Status.Contains(a.Status))
                    .WhereIf(input.BeginPlanArriveDate.HasValue, a => input.BeginPlanArriveDate.Value <= a.PlanArriveDate)
                    .WhereIf(input.EndPlanArriveDate.HasValue, a => a.PlanArriveDate <= input.EndPlanArriveDate)
                    .WhereIf(input.BeginArriveTime.HasValue, a => input.BeginArriveTime <= a.ArriveTime)
                    .WhereIf(input.EndArriveTime.HasValue, a => a.ArriveTime <= input.EndArriveTime)
                    .WhereIf(input.BeginCreateTime.HasValue, a => input.BeginCreateTime <= a.CreateTime)
                    .WhereIf(input.EndCreateTime.HasValue, a => a.CreateTime <= input.EndCreateTime);
                var totalCount = await queryResults.CountAsync();
                var pagedResults = await queryResults.ProjectTo<QueryRepairAppointmentsOutput>(_autoMapper.ConfigurationProvider)
                    .PageAndOrderBy(pageRequest).ToListAsync();
                return new Page<QueryRepairAppointmentsOutput>(pageRequest, totalCount, pagedResults);
            }
    

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

       4   AutoMapTo、AutoMapFrom

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

    [AutoMapTo(typeof(User))]
    public class CreateUserInput
    {
        public string Name { get; set; }
    
        public string Surname { get; set; }
    
        public string EmailAddress { get; set; }
    
        public string Password { get; set; }
    }
    

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

  • 相关阅读:
    Params参数的应用
    用反射动态创建委托
    Javascript中的Array的运用
    四个JS代码与jquery架构简单运用
    base 基本用法转载自(MSDN)
    Microsoft AJAX Library 新特性简单代码
    典型的委托的不同写法(转载)
    创建反射实例|反射过滤|反射搜索
    【软工】第1次个人作业
    【软工】第0次个人作业
  • 原文地址:https://www.cnblogs.com/seekdream/p/11359529.html
Copyright © 2020-2023  润新知