Asp.NetCore之AutoMapper基礎篇

跳躍的鍵盤手發表於2020-12-03

應用場景

       現在由於前後端技術的分離,後端程式設計師在使用ORM框架開發後臺API介面的時候,往往會將資料庫的“資料模型”直接提供給前端。而大多數時候,可能這些資料並不能夠滿足前端展示的需求,有時候可能需要在“資料模型”的基礎上,加幾個欄位或者改幾個欄位展示名稱或者欄位展示風格,以滿足前端“檢視模型”的需求。遇到這種情況,後端往往需要同時定義“資料模型”和“檢視模型”,並在兩者之間做大量的欄位賦值工作,如果“資料模型”和“檢視模型”差別不大的話,這無疑很耗費心力,而且容易出錯。

        我們先來理解兩個概念“資料模型”和“檢視模型”:

        “資料模型”:最簡單的一種“”資料模型“”你可以把它當成是資料庫表物件模型(資料模型欄位一一對應資料庫表結構,是資料庫表的一種表現形式),使用ORM的小夥伴應該都知道,通過ORM資料庫模型可以直接對映到資料表結構,可以直接運算元據庫。

        “檢視模型”:通過這個名稱我們很容易理解,檢視模型是前端展示頁面中所需元素的一個集合。

        當我們將“資料模型”轉換成“檢視模型”提供給前端的時候,務必會產生大量的模型欄位賦值的工作,模型很大的時候是不是想死的心都有了。。。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

相關文章