• Asp.NetCore之AutoMapper基础篇


    应用场景

           现在由于前后端技术的分离,后端程序员在使用ORM框架开发后台API接口的时候,往往会将数据库的“数据模型”直接提供给前端。而大多数时候,可能这些数据并不能够满足前端展示的需求,有时候可能需要在“数据模型”的基础上,加几个字段或者改几个字段展示名称或者字段展示风格,以满足前端“视图模型”的需求。遇到这种情况,后端往往需要同时定义“数据模型”和“视图模型”,并在两者之间做大量的字段赋值工作,如果“数据模型”和“视图模型”差别不大的话,这无疑很耗费心力,而且容易出错。

            我们先来理解两个概念“数据模型”和“视图模型”:

            “数据模型”:最简单的一种“”数据模型“”你可以把它当成是数据库表对象模型(数据模型字段一一对应数据库表结构,是数据库表的一种表现形式),使用ORM的小伙伴应该都知道,通过ORM数据库模型可以直接映射到数据表结构,可以直接操作数据库。

            “视图模型”:通过这个名称我们很容易理解,视图模型是前端展示页面中所需元素的一个集合。

            当我们将“数据模型”转换成“视图模型”提供给前端的时候,务必会产生大量的模型字段赋值的工作,模型很大的时候是不是想死的心都有了。。。AutoMapper的出现正是解决了两个模型之间枯燥无味的转换工作,用户只需要简单设置一下转换规则即可,避免了我们每次都采用手工编写代码的方式进行转换。

           本文项目源码:https://github.com/chenxf1117/Asp.NetCore-AutoMapper

    .NetCore快速上手

    1.创建项目

     

    添加数据库“数据模型”:

        /// <summary>
        /// 订单表
        /// </summary>
        public class Order
        {
            /// <summary>
            /// ID
            /// </summary>
            public int Id { get; set; }
            /// <summary>
            /// 订单名称
            /// </summary>
            public string Name { get; set; }
            /// <summary>
            /// 价格
            /// </summary>
            public decimal Price { get; set; }
    
            public DateTime CreateTime { get; set; }
    
            public int CustomId { get; set; }
    }
        /// <summary>
        /// 订单子表
        /// </summary>
        public class OrderItem
        {
            public int ItemId { get; set; }
            public int OrderId { get; set; }
            public decimal Price { get; set; }
            /// <summary>
            /// 创建时间
            /// </summary>
            public DateTime CreateTime { get; set; }
        }

     2.添加AutoMapper依赖包

    第二个包是.NetCore依赖注入使用。

    3.项目中添加“视图模型”

        /// <summary>
        /// 订单映射模型
        /// </summary>
        public class OrderDTO
        {
            public int Id { get; set; }
            /// <summary>
            /// 订单名称
            /// </summary>
            public string OrderName { get; set; }
            public decimal Price { get; set; }
            public DateTime CreateTime { get; set; }
            public int CustomId { get; set; }
        }
        /// <summary>
        /// 订单子表映射模型
        /// </summary>
        public class OrderItemDTO
        {
            public int ItemId { get; set; }
            public int OrderId { get; set; }
            public decimal Price { get; set; }
            /// <summary>
            /// 创建时间
            /// </summary>
            public string CreateTime { get; set; }
        }

     4.添加自定义AutoMapper映射文件(OrderMapperProfile)

     在.NetCore我们需要创建一个自己的映射配置类,该配置类必须继承AutoMapper的Profile抽象类,然后才能通过依赖注入AutoMapper的方式调用。

        /// <summary>
        /// 映射配置
        /// </summary>
        public class OrderMapperProfile : Profile
        {
            public OrderMapperProfile()
            {
                //字段名称不一致 Name映射到OrderName
                CreateMap<Order, OrderDTO>().ForMember(dest => dest.OrderName, src => src.MapFrom(s => s.Name));
                //字段类型不一致 DateTime映射到String
                CreateMap<OrderItem, OrderItemDTO>().ForMember(dest => dest.CreateTime, src => src.ConvertUsing(new FormatConvert()));
            }
        }
    
        /// <summary>
        /// DateTime映射到String
        /// </summary>
        public class FormatConvert : IValueConverter<DateTime, string>
        {
            public string Convert(DateTime sourceMember, ResolutionContext context)
            {
                if (sourceMember == null)
                    return DateTime.Now.ToString("yyyyMMddHHmmssfff");
                return sourceMember.ToString("yyyyMMddHHmmssfff");
            }
        }

     1)自定义映射文件需继承AutoMapper抽象类“Profile”

     2)调用方法CreateMap实现模型映射

    public IMappingExpression<TSource, TDestination> CreateMap<TSource, TDestination>(MemberList memberList);
    public IMappingExpression<TSource, TDestination> CreateMap<TSource, TDestination>();

     TSource代表“数据模型”,TDestination代表“视图模型”,MemberList可选,默认0

        public enum MemberList
        {
            /// <summary>
            /// 检查是否已映射所有目标成员
            /// </summary>
            Destination = 0,
    
            /// <summary>
            /// 检查是否已映射所有源成员
            /// </summary>
            Source = 1,
    
            /// <summary>
            /// 既不检查源成员也不检查目标成员,跳过验证
            /// </summary>
            None = 2
        }

    例如:

    CreateMap<Order, OrderDTO>();

    3)调用ForMember自定义两个模型之间映射规则。

         因为 AutoMapper 默认是通过匹配字段名称和类型进行自动匹配,所以如果你进行转换的两个类的中的某些字段名称不一样,这里我们就需要进行手动的编写转换规则。

    IMappingExpression<TSource, TDestination> ForMember<TMember>(Expression<Func<TDestination, TMember>> destinationMember, Action<IMemberConfigurationExpression<TSource, TDestination, TMember>> memberOptions);

         其中比较常用的:

          1.两个映射模型属性/字段名称不一致。

    //字段名称不一致 源类型Name映射到目标类型OrderName
    CreateMap<Order, OrderDTO>().ForMember(dest => dest.OrderName, src => src.MapFrom(s => s.Name));

          2.两个映射模型属性/字段数据类型或展现形式不一致。

    //字段类型不一致 源类型DateTime映射到目标类型String字符串
    CreateMap<OrderItem, OrderItemDTO>().ForMember(dest => dest.CreateTime, src => src.ConvertUsing(new FormatConvert()));

    ConvertUsing()方法中需实现成员参数接口IValueConverter中的Convert方法,实现自定义转换

    void ConvertUsing<TSourceMember>(IValueConverter<TSourceMember, TMember> valueConverter);
        public interface IValueConverter<in TSourceMember, out TDestinationMember>
        {
            TDestinationMember Convert(TSourceMember sourceMember, ResolutionContext context);
        }

    自定义实现接口Convert方法,实现DateTime转string字符串:

        /// <summary>
        /// DateTime映射到String
        /// </summary>
        public class FormatConvert : IValueConverter<DateTime, string>
        {
            public string Convert(DateTime sourceMember, ResolutionContext context)
            {
                if (sourceMember == null)
                    return DateTime.Now.ToString("yyyyMMddHHmmssfff");
                return sourceMember.ToString("yyyyMMddHHmmssfff");
            }
        }

    5.配置好映射文件后,.NetCore中需要注册我们刚才的自定义Profile

     //注册AutoMapper
     //方式1  指定映射配置文件  多个可用数组表示
     services.AddAutoMapper(typeof(OrderMapperProfile));
     //方式2   注册程序集下面所有映射文件
     services.AddAutoMapper(Assembly.Load("程序集dll名称"))

    6.最后我们可以通过注入AutoMapper的方式,在业务逻辑层使用我们的自定义映射配置文件Profile

    public class OrderService : IOrderService
        {
            private readonly IMapper mapper;
            /// <summary>
            /// 构造函数注入Mapper
            /// </summary>
            /// <param name="_dBContext"></param>
            /// <param name="_mapper"></param>
            public OrderService(IMapper _mapper)
            {
                mapper = _mapper;
            }
            /// <summary>
            /// 名称不一致  映射
            /// </summary>
            /// <returns></returns>
            public async Task<List<OrderDTO>> Query()
            {
                //ORM获取数据模型数据
                var orderList = await dBContext.DB.Queryable<Order>().ToListAsync();
                //DTO映射
                var orderDtoList = mapper.Map<List<OrderDTO>>(orderList);
                return await Task.FromResult(orderDtoList);
            }
    
            /// <summary>
            /// 数据类型/展现形式不一致 映射
            /// </summary>
            /// <returns></returns>
            public async Task<List<OrderItemDTO>> QueryItem()
            {
                var orderList = await dBContext.DB.Queryable<OrderItem>().ToListAsync();
                var orderDtoList = mapper.Map<List<OrderItemDTO>>(orderList);
                return await Task.FromResult(orderDtoList);
            }
        }

    7.实现效果

     1)“数据模型”Order类型中的Name属性值  映射到  “视图模型”OrderDTO类型中的OrderName

     

      2)“数据模型”OrderItem类型中的CreateTime属性DateTime数据类型  映射到  “视图模型”OrderItemDTO类型中的CreateTime属性string数据类型

     小结

          本篇文章主要简单介绍下在Asp.NetCore项目中如何快速上手AutoMapper,总体来看想要上手还是比较简单,只要掌握好几个经常使用的映射规则就可以了。当然,AutoMapper为我们准备了各种自定规则的入口,有兴趣的小伙伴可以下载源码,源码中包含了很多测试用例,学习起来也比较容易理解。附上AutoMapper源码地址:https://github.com/AutoMapper/AutoMapper

  • 相关阅读:
    从国内流程管理软件市场份额看中国BPM行业发展
    为什么流程是成功企业的关键?
    港真,到底应该选择OA还是BPM?
    hello world
    i40e网卡驱动遇到的一个问题
    suse 11 sp3编译报错问题
    linux scsi相关的一些学习笔记
    linux tcp 在timewait 状态下的报文处理
    趋势科技 redirfs模块的一个小问题
    linux 3.10 一个扇区异常可能引发的hung
  • 原文地址:https://www.cnblogs.com/chenxf1117/p/14069111.html
Copyright © 2020-2023  润新知