主頁 > .NET開發 > 在 ASP.NET Core 專案中使用 AutoMapper 進行物體映射

在 ASP.NET Core 專案中使用 AutoMapper 進行物體映射

2020-09-23 03:08:47 .NET開發

 一、前言

  在實際專案開發程序中,我們使用到的各種 ORM 組件都可以很便捷的將我們獲取到的資料系結到對應的 List<T> 集合中,因為我們最終想要在頁面上展示的資料與資料庫物體類之間可能存在很大的差異,所以這里更常見的方法是去創建一些對應于頁面資料展示的 `視圖模型` 類,通過對獲取到的資料進行二次加工,從而滿足實際頁面顯示的需要,

  因此,如何更便捷的去實作 資料庫持久化物件 與 視圖物件 間的物體映射,避免我們在代碼中去一次次的手工實作這一程序,就可以降低開發的作業量,而 AutoMapper 則是可以幫助我們便捷的實作物體轉換這一程序的利器,所以,本章我們就來學習如何在 ASP.NET Core 專案中通過使用 AutoMapper 去完成物體間的映射,

  當然,如果你習慣于從視圖展現到持久化到資料庫都采用資料庫物體,那么本篇文章對你可能不會有任何的幫助,

  代碼倉儲:https://github.com/Lanesra712/ingos-common/tree/master/sample/aspnetcore/aspnetcore-automapper-tutorial

 二、Step by Step

  AutoMapper 是一個 OOM(Object-Object-Mapping) 組件,從名字上就可以看出來,這一系列的組件主要是為了幫助我們實作物體間的相互轉換,從而避免我們每次都采用手工撰寫代碼的方式進行轉換,在沒有采用 OOM 組件之前,如果我們需要實作類似于一份資料在不同客戶端顯示不同的欄位,我們只能以手工的、逐個屬性賦值的方式實作資料在各個客戶端資料型別間的資料傳遞,而 OOM 組件則可以很方便的幫我們實作這一需求,

  1、幾個概念

  在上面我們有提到 資料庫持久化物件 和 視圖物件 這兩個概念,其實除了這兩個物件的概念之外,還存在一個 資料傳輸物件 的概念,這里我們來簡單闡述下這三種物件的概念,

  資料庫持久化物件(Persistent Object):顧名思義,這個物件是用來將我們的資料持久化到資料庫,一般來說,持久化物件中的欄位會與資料庫中對應的 table 保持一致,

  這里,如果你采用了 DDD 的思想去指導設計系統架構,其實最終落地到我們代碼中的其實是 領域物件(Domain Object),它與 資料庫持久化物件 最顯著的差異在于 領域物件 會包含當前業務領域的各種事件,而 資料庫持久化物件 僅是包含了資料庫中對應 table 的資料欄位資訊,

  視圖物件(View Object):視圖物件 VO 是面向前端用戶頁面的,一般會包含呈現給用戶的某個頁面/組件中所包含的所有資料欄位資訊,

  資料傳輸物件(Data Transfer Object):資料傳輸物件 DTO 一般用于前端展示層與后臺服務層之間的資料傳遞,以一種媒介的形式完成 資料庫持久化物件 與 視圖物件 之間的資料傳遞,

  這里通過一個簡單的示意圖去解釋下這三種物件的具體使用場景,在這個示例的專案中,我省略了資料傳輸物件,將資料庫持久化物件直接轉換成頁面顯示的視圖物件,

  2、組件加載

  首先我們需要通過 Nuget 將 AutoMapper 加載到專案中,因為這個示例專案只包含一個 MVC 的專案,并沒有多余的分層,所以這里需要將兩個使用到的 dll 都添加到這個 MVC 專案中,

Install-Package AutoMapper
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection

  這里我添加了 AutoMapper.Extensions.Microsoft.DependencyInjection 這個程式集,從這個程式集的名字就可以看出來,這個程式集主要是為了我們可以通過依賴注入的方式在專案中去使用 AutoMapper,

  在 .NET Fx 的時代,我們使用 AutoMapper 時,可能就像下面的代碼一樣,更多的是通過 Mapper 的幾個靜態方法來實作物體間的映射,不過在 .NET Core 程式中,我們首選還是采用依賴注入的方式去完成物體間的映射,

// 構建物體映射規則
Mapper.Initialize(cfg => cfg.CreateMap<OrderModel, OrderDto>());

// 物體映射
var order = new OrderModel{};
OrderDto dto = Mapper.Map<OrderModel,OrderDto>(order);

  3、使用案例

  因為原本想要使用的示例專案是之前的 ingos-server 這個專案,由于目前自己有在學習 DDD 的知識,并且有在按照微軟的 eShopOnContainers 這個專案中基于 DDD 思想設計的框架,對自己的這個 ingos-server 專案進行 DDD 化的調整,嗯,其實就是照葫蘆畫瓢,所以目前整個專案被我改的亂七八糟的,不太適合作為示例專案了,所以這里新創建了一個比較單純的 ASP.NET Core MVC 專案來作為這篇文章的演示專案,

  因為這個示例專案只是為了演示如何在 ASP.NET Core 專案中去使用 AutoMapper,所以這里并沒有進行分層,整個示例頁面的運行流程就是,PostController 中的 List Action 呼叫 PostAppService 類中的 GetPostLists 方法去獲取所有的文章資料,同時在這個方法中會進行物體映射,將我們從 PostDomain 中獲取到的 PO 物件轉換成頁面展示的 VO 物件,專案中每個檔案夾的作用見下圖所示,

  這里的示例專案是演示當我們從資料庫獲取到需要的資料后,如何完成從 PO 到 VO 的物體映射,PostModel(PO)和 PostViewModel(VO)的類定義如下所示,

public class PostModel
{
    public Guid Id { get; set; }
    public long SerialNo { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    public string Image { get; set; }
    public short CategoryCode { get; set; }
    public bool IsDraft { get; set; }
    public string Content { get; set; }
    public DateTime ReleaseDate { get; set; }
    public virtual IList<CommentModel> Comments { get; set; }
}

public class PostViewModel
{
    public Guid Id { get; set; }
    public long SerialNo { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    public short CategoryCode { get; set; }
    public string Category => CategoryCode == 1001 ? ".NET" : "雜談";
    public string ReleaseDate { get; set; }
    public short CommentCounts { get; set; }
    public virtual int Count { get; set; }
}

  首先我們需要創建一個物體映射的配置類,需要繼承于 AutoMapper 的 Profile 類,在無參建構式中,我們就可以通過 CreateMap 方法去創建兩個物體間的映射關系,

public class PostProfile : Profile
{
    /// <summary>
    /// ctor
    /// </summary>
    public PostProfile()
    {
        // 配置 mapping 規則
        //
        CreateMap<PostModel, PostViewModel>();
    }
}

  通過泛型的 CreateMap 方法就可以完成我們從 PostModel(PO) 到 PostViewModel(VO) 的物體映射,當然,因為 AutoMapper 默認是通過匹配欄位名稱和型別進行自動匹配,所以如果你進行轉換的兩個類的中的某些欄位名稱不一樣,這里我們就需要進行手動的撰寫轉換規則,

  就像在這個需要進行物體映射的示例代碼中,PostViewModel 中的 CommentCounts 欄位是根據 PostModel 中 CommentModel 集合的資料個數進行賦值的,所以這里我們就需要對這個欄位的轉換規則進行修改,

  在 AutoMapper 中,我們可以通過 ForMember 方法對映射規則做進一步的加工,這里我們需要指明 PostViewModel 的 CommentCounts 欄位的值是通過對 PostModel 中的 Comments 資訊進行求和從而獲取到的,最終實作的轉換代碼如下所示,

public class PostProfile : Profile
{
    /// <summary>
    /// ctor
    /// </summary>
    public PostProfile()
    {
        // 配置 mapping 規則
        //
        CreateMap<PostModel, PostViewModel>()
            .ForMember(destination => destination.CommentCounts, source => source.MapFrom(i => i.Comments.Count()));
    }
}

  ForMember 方法不僅可以進行指定不同名稱的欄位進行轉換,也可以通過撰寫規則實作欄位型別的轉換,例如這里 PO 中的 ReleaseDate 欄位其實是 DateTime 型別的,我們需要通過撰寫規則將該欄位對應到 VO 中 string 型別的 ReleaseDate 欄位上,最終的實作代碼如下所示,

public class PostProfile : Profile
{
    /// <summary>
    /// ctor
    /// </summary>
    public PostProfile()
    {
        // Config mapping rules
        //
        CreateMap<PostModel, PostViewModel>()
            .ForMember(destination => destination.CommentCounts, source => source.MapFrom(i => i.Comments.Count()))
            .ForMember(destination => destination.ReleaseDate, source => source.ConvertUsing(new DateTimeConverter()));
    }
}

public class DateTimeConverter : IValueConverter<DateTime, string>
{
    public string Convert(DateTime source, ResolutionContext context)
        => source.ToString("yyyy-MM-dd HH:mm:ss");
}

  這里很多人可能習慣將所有的物體映射規則都放到同一個 Profile 檔案里面,因為這里采用是單體架構的專案,所以整個專案中會存在不同的模塊,所以這里我是按照每個模塊去創建對應的 Profile 檔案,實際在 ingos-server 這個專案中的使用方式見下圖所示,

   當我們創建好對應的映射規則后,因為我們是采用依賴注入的方式進行使用,所以這里我們就需要將我們的匹配規則注入到 IServiceCollection 中,從之前加載的程式集的 github readme 描述中可以看到,我們需要將配置好的 Profile 類通過 AddAutoMapper 這個擴展方法進行注入,

  因為我們在實際專案中可能存在多個自定義的 Profile 檔案,而我們肯定是需要將這些自定義規則都注入到 IServiceCollection 中,所以我在 AddAutoMapper 這個方法的基礎上創建了一個 AddAutoMapperProfiles 方法去注入我們的物體映射規則,

  通過 AutoMapper 的說明我們可以看出來,所有的自定義的 Profile 類都是需要繼承于 AutoMapper 的 Profile 基類,所以這里我是采用反射的方式,通過獲取到程式集中所有繼承于 Profile 類的類檔案進行批量的注入到 IServiceCollection 中,具體的實作代碼如下所示,

/// <summary>
/// Automapper 映射規則配置擴展方法
/// </summary>
public static class AutoMapperExtension
{
    public static IServiceCollection AddAutoMapperProfiles(this IServiceCollection services)
    {
        // 從 appsettings.json 中獲取包含配置規則的程式集資訊
        string assemblies = ConfigurationManager.GetConfig("Assembly:Mapper");

        if (!string.IsNullOrEmpty(assemblies))
        {
            var profiles = new List<Type>();

            // 獲取繼承的 Profile 型別資訊
            var parentType = typeof(Profile);

            foreach (var item in assemblies.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries))
            {
                // 獲取所有繼承于 Profile 的類
                //
                var types = Assembly.Load(item).GetTypes()
                    .Where(i => i.BaseType != null && i.BaseType.Name == parentType.Name);

                if (types.Count() != 0 || types.Any())
                    profiles.AddRange(types);
            }

            // 添加映射規則
            if (profiles.Count() != 0 || profiles.Any())
                services.AddAutoMapper(profiles.ToArray());
        }

        return services;
    }
}

  因為我是將需要加載的程式集資訊放到組態檔中的,所以這里我們只需要將包含 Profile 規則的程式集添加到對應的配置項下面就可以了,此時如果包含多個程式集,則需要使用 `|` 進行分隔,

{
  "Assembly": {
    "Mapper": "aspnetcore-automapper-tutorial"
  }
}

  當我們將所有的物體映射規則注入到 IServiceCollection 中,就可以在代碼中使用這些物體映射規則,和其它通過依賴注入的介面使用方式相同,我們只需要在使用到的地方注入 IMapper 介面,然后通過 Map 方法就可以完成物體間的映射,使用的代碼如下,

public class PostAppService : IPostAppService
{
    #region Initialize

    /// <summary>
    ///
    /// </summary>
    private readonly IPostDomain _post;

    /// <summary>
    ///
    /// </summary>
    private readonly IMapper _mapper;

    /// <summary>
    /// ctor
    /// </summary>
    /// <param name="post"></param>
    /// <param name="mapper"></param>
    public PostAppService(IPostDomain post, IMapper mapper)
    {
        _post = post ?? throw new ArgumentNullException(nameof(post));
        _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
    }

    #endregion Initialize

    /// <summary>
    /// 獲取所有的文章資訊
    /// </summary>
    /// <returns></returns>
    public IList<PostViewModel> GetPostLists()
    {
        var datas = _post.GetPostLists();
        return _mapper.Map<IList<PostModel>, IList<PostViewModel>>(datas);
    }
}

  至此我們就實作了在 ASP.NET Core 專案中使用 AutoMapper,實作后的結果如下圖所示,

 三、總結

  本篇文章主要是演示下如何在 ASP.NET Core 專案中去使用 AutoMapper 來實作物體間的映射,因為之前只是在 .NET Fx 專案中有使用過這個組件,并沒有在 .NET Core 專案中使用,所以這次趁著國慶節假期就來嘗試如何在 .NET Core 專案中使用,整個組件使用起來其實是很簡單的,但是使用后卻可以給我們在實際的專案開發中省很多的事,所以就把自己的使用方法分享出來,如果對你有些許的幫助的話,不勝榮幸~~~

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/108767.html

標籤:.NET Core

上一篇:EF Core 實作讀寫分離的最佳方案

下一篇:.NET開發者必須學習.NET Core

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more