16

Asp.NetCore之AutoMapper基础篇

 3 years ago
source link: http://www.cnblogs.com/chenxf1117/p/14069111.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

应用场景

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

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

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

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

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

.NetCore快速上手

1.创建项目

myaMJbU.png!mobile

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

    /// <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依赖包

YJfIvqJ.png!mobile

第二个包是.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

iieEjiN.png!mobile

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

FzQz6v7.png!mobile

小结

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK