.NET Core實戰項目之CMS 第三章 入門篇-源碼解析配置文件及依賴註入

来源:https://www.cnblogs.com/yilezhu/archive/2018/11/21/9998021.html
-Advertisement-
Play Games

作者:依樂祝 原文鏈接:https://www.cnblogs.com/yilezhu/p/9998021.html 寫在前面 上篇文章我給大家講解了ASP.NET Core的概念及為什麼使用它,接著帶著你一步一步的配置了.NET Core的開發環境並創建了一個ASP.NET Core的mvc項目, ...


作者:依樂祝
原文鏈接:https://www.cnblogs.com/yilezhu/p/9998021.html

寫在前面

上篇文章我給大家講解了ASP.NET Core的概念及為什麼使用它,接著帶著你一步一步的配置了.NET Core的開發環境並創建了一個ASP.NET Core的mvc項目,同時又通過一個實戰教你如何在頁面顯示一個Content的列表。不知道你有沒有跟著敲下代碼,千萬不要做眼高手低的人哦。這篇文章我們就會設計一些複雜的概念了,因為要對ASP.NET Core的啟動及運行原理、配置文件的載入過程進行分析,依賴註入,控制反轉等概念的講解等。俗話說,授人以魚不如授人以漁,所以文章旨在帶著大家分析源碼,讓大家能知其然更能知其所以然。為了偷懶,繼續使用上篇文章的例子了!有興趣的朋友可以加群637326624相互交流!
再次感謝張隊的審稿!

本文已收錄至.NET Core實戰項目之CMS 第一章 入門篇-開篇及總體規劃 點擊可以查看更多教程。

ASP.NET Core啟動源碼解析

這部分我就帶著大家一起看下asp.net core項目的運行流程吧!順帶著瞭解下asp.net core的運行原理,說的不好的話,希望大家給以指正,從而能夠正確的幫助更多的人。

  1. 首先上一下上篇文章的項目結構吧,如下所示,熟悉C#的朋友應該知道,要找程式的入庫,那麼就應該找到Main方法。而asp.net core的main方法就在Program.cs文件中。

    1542771232412

  2. 打開後看到如下的代碼,我加了註釋,大伙將就看下,下麵我們來一步一步的分析

    /// <summary>
            /// Main方法,程式的入口方法
            /// </summary>
            /// <param name="args"></param>
            public static void Main(string[] args)
            {
                CreateWebHostBuilder(args)//調用下麵的方法,返回一個IWebHostBuilder對象
                    .Build()//用上面返回的IWebHostBuilder對象創建一個IWebHost
                    .Run();//運行上面創建的IWebHost對象從而運行我們的Web應用程式換句話說就是啟動一個一直運行監聽http請求的任務
            }
    
            public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
                WebHost.CreateDefaultBuilder(args)//使用預設的配置信息來初始化一個新的IWebHostBuilder實例
                    .UseStartup<Startup>();// 為Web Host指定了Startup類

    可以看到asp.net core程式實際上就是一個控制台程式,運行一個webhost對象從而啟動一個一直運行的監聽http請求的任務。所以我們的重點就是分析一下這個WebHost創建的過程:
    創建IWebHostBuilder-》創建IWebHost-》然後運行創建的IWebHost。

  3. 這裡我們從IWebHostBuilder的Build分析下創建的過程,有興趣的朋友可以看下,沒興趣的朋友可以直接跳到下一個步驟繼續閱讀。

    1. 首先到aspnetcore的github開源地址https://github.com/aspnet/AspNetCore/tree/release/2.1 上去下載源碼(我們使用的是2.1)。然後使用vscode打開解壓後的文件夾。至於vscode如何載入文件,你可以看我這篇文章使用Visual Studio Code開發.NET Core看這篇就夠了 當然你也可以在上面的網頁上直接找到相應的目錄瀏覽也是可以的。(看結構好像是使用vscode進行開發的)

    2. 根據IWebHostBuilder的命名空間我們找到了它的實現,路徑為src/Hosting/Hosting/src/WebHostBuilder.cs

      1542782994757

    3. 通過上面的代碼我們可以看到首先是通過BuildCommonServices來構建一個ServiceCollection。為什麼說這麼說呢,先讓我們我們跳轉到BuidCommonServices方法中看下吧。

      1542784242634

      可以看到,var services = new ServiceCollection();首先new一個ServiceCollection然後往services裡面註入很多內容,比如:WebHostOptions ,IHostingEnvironment ,IHttpContextFactory ,IMiddlewareFactory 等等(其實這裡已經設計到依賴註入的概念了,先思考下吧),然後我們在後續就可以使用了!最後這個BuildCommonServices就返回了這個services對象。

    4. 在上面的依賴註入中有一個方法,不知道大家註意到沒有,因為我們在步驟2貼出的代碼裡面有一個UseStartup<Startup>() 其實在上面的BuildCommonServices方法中也有對IStartup的註入的。首先,判斷Startup類是否繼承於IStartup介面,如果是繼承的,那麼就可以直接加入在services 裡面去,如果不是繼承的話,就需要通過ConventionBasedStartup(methods)把method轉換成IStartUp後註入到services裡面去。結合上面我們的代碼,貌似我們平時用的時候註入的方式都是採用後者。

    5. 我們再回到build方法拿到了BuildCommonServices方法構建的ServiceCollection實例後,通過GetProviderFromFactory(hostingServices) 方法構造出了IServiceProvider 對象。到目前為止,IServiceCollection和IServiceProvider都拿到了。然後根據IServiceCollection和IServiceProvider對象構建WebHost對象。構造了WebHost實例還不能直接返回,還需要通過Initialize對WebHost實例進行初始化操作。那我們看看在初始化函數Initialize中,都做了什麼事情吧。

      1542786448255

    6. 這裡我們把代碼導航到src/Hosting/Hosting/src/Internal/WebHost.cs找到Initialize方法。如下圖所示:主要就是一個EnsureApplicationServices 方法。1542787078949

    7. 我們繼續導航查看這個方法的內容如下:就是拿到Startup 對象,然後把_applicationServiceCollection 中的對象註入進去。

      1542787219703

    8. 至此我們build中註冊的對象以及StartUp中註冊的對象都已經加入到依賴註入容器中了,接下來就是Run起來了。這個run的代碼在src\Hosting\Hosting\src\WebHostExtensions.cs中,代碼如下:1542787884205WebHost執行RunAsync運行web應用程式並返回一個只有在觸發或關閉令牌時才完成的任務(這裡又涉及到非同步編程的知識了,咱們以後再詳細講解) 。這就是我們運行ASP.Net Core程式的時候,看到的那個命令行視窗了,如果不關閉視窗或者按Ctrl+C的話是無法結束的。

    9. 至此啟動的過程的源碼分析完成了。

    配置文件

    上面給大家介紹了ASP.NET Core的啟動過程,中間牽扯到了一些依賴註入的概念。關於依賴註入的概念呢,我們後面再說,這裡先給大家講解下配置文件的載入過程。
  4. 打開上篇文章我們創建的項目,併在appsettings.json裡面加入如下內容:

    {
      "Logging": {
        "LogLevel": {
          "Default": "Warning"
        }
      },
      "Content": {
        "Id": 1,
        "title": "title1",
        "content": "content1",
        "status": 1,
        "add_time": "2018-11-21 16:29",
        "modify_time": null
      },
      "AllowedHosts": "*"
    }
    
  5. 然後在Startup類中ConfigureServices中註冊TOptions對象如下所示:

     services.Configure<Content>(Configuration.GetSection("Content"));//註冊TOption實例對象

    這段代碼也就是從appsettings.json這個配置文件中的Content這個節點匹配到Content這個對象上。

  6. 修改下ContentController這個控制器代碼如下:

    private readonly Content contents;
            public ContentController(IOptions<Content> option)
            {
                contents = option.Value;
            }
            /// <summary>
            /// 首頁顯示
            /// </summary>
            /// <returns></returns>
            public IActionResult Index()
            {
    
                return View(new ContentViewModel { Contents=new List<Content> { contents} });
            }
  7. 按下F5運行下,然後導航到Content目錄看到如下頁面:說明成功從appsettings.json這個文件中載入了內容。這一切是怎麼發生的呢?下麵我們就一步一步的來分析。

    1542792298052

  8. 我們回過頭來看我們的Main方法,發現裡面有一個CreateDefaultBuilder方法,就是這個方法裡面為我們做了一些預設的設置,然後載入我們的配置文件的!

    1542801612869

  9. 我們在源碼裡面找到CreateDefaultBuilder 的源碼(反正我找了半天,起初在Hosting下麵找,實際上在MetaPackages下麵的),位置在src\MetaPackages\src\Microsoft.AspNetCore\WebHost.cs 有的人可能找不到哦,可以看到這個方法會在ConfigureAppConfiguration 的時候預設載入appsetting文件,並做一些初始的設置,所以我們不需要任何操作,就能載入appsettings 的內容了。

    1542801370627

  10. 既然知道了原理後,我們就試著重寫下這個ConfigureAppConfiguration 然後載入我們自定義的json文件吧。

  11. 滑鼠右鍵新建一個Content.json文件,然後輸入如下的內容:

    {
      "ContentList": 
        {
          "Id": 1,
          "title": "title1 from diy json",
          "content": "content1 from diy json",
          "status": 1,
          "add_time": "2018-11-21 16:29",
          "modify_time": null
        }
    
    }
    
  12. 然後打開Program.cs。按如下代碼進行改造:

    /// <summary>
            /// Main方法,程式的入口方法
            /// </summary>
            /// <param name="args"></param>
            public static void Main(string[] args)
            {
                CreateWebHostBuilder(args)//調用下麵的方法,返回一個WebHostBuilder對象
                    .Build()//用上面返回的WebHostBuilder對象創建一個WebHost
                    .Run();//運行上面創建的WebHost對象從而運行我們的Web應用程式換句話說就是啟動一個一直運行監聽http請求的任務
            }
    
            public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
                WebHost.CreateDefaultBuilder(args)//使用預設的配置信息來初始化一個新的IWebHostBuilder實例
                .ConfigureAppConfiguration((hostingContext, config) =>
                {
                    var env = hostingContext.HostingEnvironment;
    
                    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                          .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true)
                          .AddJsonFile("Content.json",optional:false,reloadOnChange:false)
                          .AddEnvironmentVariables();
    
                })
                .UseStartup<Startup>();// 為Web Host指定了Startup類

    1542804422257

  13. 然後Startup裡面ConfigureServices中的代碼修改如下:

    1542804670714

  14. 然後按下F5運行下代碼吧,如下圖所示,從我們最新添加的json文件中載入出來數據了。

    1542804899927

  15. 這裡多講一點,傳統asp.net的web.config文件如果有更改的話是必須要重啟站點才能使,配置文件生效的,但是asp.net core的配置文件是支持熱更新的,及不重啟網站也能載入更新,只需要設置一下屬性即可,如下圖所示:

    1542805102992

  16. 配置文件的源碼解讀這塊就到這裡了。下麵開始依賴註入的講解。

依賴註入與控制反轉

如果大家仔細閱讀文章的話,相信已經看出來了,我上面提到過好幾次依賴註入的概念。那麼究竟什麼是依賴註入呢?下麵我們就拿我們上面的ContentController來好好的來理解下。
依賴註入:當一個對象ContentController需要另一個對象Content來協同完成任務的時候,那麼這個ContentController就對這個Content對象產生了依賴關係。那麼在這個ContentController中,是怎麼註入的呢?就是從控制器中註入的了,如下圖所示:

1542805959559

從asp.net 轉過來的你是不是想起了之前的千篇一律的new對象啊。沒對象自己new(要是女朋友也能new多好啊……)當然除了單例對象,靜態哈。

這裡又設計一個概念就是控制反轉。

那麼什麼是控制反轉呢?你上面看到沒有,你自己new對象就是整轉,因為你自己創建自己所要使用的對象,。那麼這種不需要你自己new對象,而是直接傳進來就是控制反轉了。(不知道比喻的恰不恰當哈)

依賴註入與控制反轉你是否已經瞭解了呢,喜歡思考的朋友可能會問了,那這個構造函數裡面的IOptions<Content> option 又是怎麼出來的?這裡就要引入一個容器的概念了。

什麼是容器呢?

這裡創建IOptions<Content> option 這個對象的東西就是容器。還記得上面我們分析源碼的時候,IServiceCollection 裡面註入了很多東西嗎?其實就是往IServiceCollection 這個容器裡面註入方法,這樣其他地方使用的時候就能自動註入了。

這就是容器的好處,由容器來統一管理實例的創建和銷毀,你只需要關心怎麼用就行了,不需要關係怎麼創建跟銷毀。

當然容器創建的實例都是有生命周期的,。下麵羅列一下,就不過多的講解了。

  • Transient: 每一次訪問都會創建一個新的實例
  • Scoped: 在同一個Scope內只初始化一個實例 ,可以理解為( 每一個request級別隻創建一個實例,同一個http request會在一個 scope內)
  • Singleton :整個應用程式生命周期以內只創建一個實例

使用的方式也很簡單,我會在接下來的課程中詳細的通過實例來進行講解!因為現在的例子還沒發演示。

總結

本文一步一步帶著你先分析了ASP.NET Core的啟動過程及運行的原理,緊接著給你講了配置文件的載入過程及原理,並通過示例代碼演示瞭如何載入自定義的配置文件,最後引出了依賴註入以及控制反轉的概念,並通過對我們上面例子的分析來緊身對依賴註入以及控制反轉的理解。至此讓你知其然更知其所以然。對ASP.NET Core的原理相信你已經瞭然於胸了!有問題的小伙伴可以加群637326624討論。那麼接下來讓我們再準備下dapper,vue以及git的快速入門就開始我們的asp.net core cms的實戰課程吧!還是那句話基礎很重要,基礎打好,後面才能事半功倍。謝謝大家。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1. 例子 1. 做一個鴨子模擬器,裡面有很多不同的鴨子,有的可以游泳,有的可以睡覺,有的可以呱呱叫,一般套路是定義一個鴨子的超類,在 超類里定義睡覺,游泳,呱呱叫的方法,再讓不同的鴨子子類繼承這個超類,實現自己的display()方法來表現鴨子的行為,像下麵這樣: 2. 但如果要加一個可以吃火鍋的 ...
  • 系統架構設計師-軟體水平考試高級-理論-資料庫。其中涉及資料庫模式(三級抽象,兩層映射),數據模型,關係代數,規範化理論,事務處理等。 ...
  • 原型模式 prototype也是常用的一種設計模式,java中已經內置,藉助於Object的clone方法,本文對原型模式進行了簡單介紹,以及Java實現,並且介紹了對象的深拷貝與淺拷貝。 ...
  • 一、概念 工廠方法模式:用來封裝對象的創建。工廠方法模式(Factory Method Pattern)通過讓 子類 決定該創建的對象是什麼,來達到將對象創建的過程封裝的目的。這樣,關於超類的代碼和子類創建對象的代碼之間就解耦了。 角色:  1、抽象工廠(Creator):定義了一個抽象的 ...
  • 有時候,當一個重要的項目進展不順利時,就有了重新開始的願望。有時這來自管理層,但通常來自開發人員自己。他們說,如果他們只有第二次機會,並且可以重新開始,那麼他們可以建立正確的系統。 但這幾乎從未發生過。從我這拿走。我見過公司多次嘗試,我可以毫無例外地說,當一個團隊開始用基本相同的方法重建相同的系統時 ...
  • 基於隊列和基於消息的TTL TTL是time to live 的簡稱,顧名思義指的是消息的存活時間。rabbitMq可以從兩種維度設置消息過期時間,分別是隊列和消息本身。 隊列消息過期時間 Per Queue Message TTL: 通過設置隊列的x message ttl參數來設置指定隊列上消息 ...
  • 縱覽武俠江湖,制勝法門不外兩項,內功和外功。二者得一可天下去得,但最終皆入內外兼修之境倚天是自內而外,先修內功九陽真經,然後以此為基礎,加上太極拳和太極劍,最終成就天下第一高手笑傲是自外而內,先學獨孤九劍,後學吸星大法,最後學易筋經。神雕也不外如是,玉女心經算是外功,內功則是獨孤求敗之法門。只修內不 ...
  • 如果這是第二次看到我的文章,歡迎右側掃碼訂閱我喲~ > 本文長度為3319字,建議閱讀9分鐘。 閱讀目錄 熔斷是什麼 熔斷怎麼做 做熔斷的最佳實踐 總結 熔斷是什麼 熔斷怎麼做 做熔斷的最佳實踐 總結 熔斷是什麼 熔斷怎麼做 做熔斷的最佳實踐 總結 熔斷是什麼 熔斷怎麼做 做熔斷的最佳實踐 總結 熔 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...