ASP.NET Core 與 .Net Core, .Net Platform Standard, 以及 Shared Runtime 之間的關係

  • 2475
  • 0
  • 2017-11-09

原本打算這周末撰寫基礎建設中下一個主題,也就是 Logging 的內容,但由於 RC2 已經進入最後階段,有些東西比 Logging 還更重要需要先介紹,所以這次的文章內容就切到與 .net core 有關的內容.這是 RC2 裡改變相當大的東西,要了解它才對你撰寫 ASP.NET Core 程式有幫助.  

[2017.07.23] .Net Platform Standard 已更名為 .Net Standard, 我有另一篇文章做更多的介紹.

這星期可精彩地忙碌了,ASP.NET Core 進入了 RC2 的 ask mode,每個工程師都在修正與檢視要在 RC2 釋出的所有功能.所謂的 ask mode 是指把 source control 上的開發用的 branch 合併到釋出用的 branch,所以除非是緊急或等級高的問題,否則其他的問題修正或新功能將不會進入到釋出用的 branch,也就是說 RC2 該有的功能都已經確定了.這次 RC2 最大的改變莫過於採用了 .net core cli.

歷史

在 ASP.NET 要進入新一個版本時,內部已經討論過這個版本需要有重大的改變,其中跨平台便是一個相當大的項目.為了要達成這一個目標,那表示 ASP.NET 的 runtime 不能再依靠 Windows,因此 ASP.NET team 自行設計了一套新的跨平台用的工具指令,它的名字叫 .net execution environment,執行的引擎就稱為 .net execution engine.也就是大家簡稱的 DNX.從 beta1 一直到 RC1 都一直是採用 DNX 的方式在運行 .net core,相關的工具鏈也都是依靠它來運作.一直到去年年底時,公司組織內的高層有異動,所有製作 ASP.NET 產品線相關的團隊被併入到開發者工具部門,從那時開始,事情有了轉變.在開發者工具部門中有 .net core cli 的團隊,他們做的工作其實原理上跟 DNX 是很類似的.後來的結果就如大家在 RC2 版本將會看到的,DNX沒有了,取而代之的是 .net core cli,所以整個 ASP.NET 相關的工具鏈也都要重新檢視然後更改.這是一個蠻大的工程,因此 RC2 才會一直延後到現在.所以,若你以前曾玩過 RC1 和 beta 版本的話,請把跟 DNX 忘了吧.你該學的是如何使用 .net core cli 的指令.

.NET Core

我不是 .net core 團隊的成員,也沒研讀過 .net core 的程式碼,所以我無法提供程式運作的細節.因此,我在這邊分享的 .net core 內容是這幾個月從 pm 那邊聽來的.整個 .net core 的內容都是 open source,包含 compiler, framework, clr 等等,如果你對原始碼有興趣,你可以到 GitHub 查看 (http://www.github.com/dotnet/).

資料來源 : 從某一個 pm 的演講裡偷來的投影片 :psource: 從某一個 pm 的演講投影片偷來的

.net core 裡面基本上分為四個東西:

1. CoreFx,也就是核心的 framework,基本上如果你要寫跨平台的 .net 程式碼時,假設你選擇採用 .net core,那麼 core framework 就是你的開發 sdk.基本上,你可以把它視為 Windows 版上的 .net framework (在內部俗稱為 full framework) 的縮小版.雖然說是縮小版,但也沒那麼小,畢竟你以前用的 .net API 在這邊都還是可以找到.比如一些基本的 data type, class library,或像網路,資料庫,IO等等方式的 API 都仍是有的.但有一些跟 Windows 有關的 api 就不會有,比如像 WPF, WCF 之類,因為這些東西是綁在 windows 上,無法跨平台.接著你可能會馬上問,如果沒用到這些 windows 依靠度很高的 API,那是不是以前寫的 .net code 都可以直接用 core framework.這想法是對的,但並非完成正確.原因是並非所有的 api (包含 property, constructor, method) 都被移轉到 core framework 上.比如說, StreamWriter 在 .net framework 上有提供一個 overload constructor 可以直接讓你帶檔案的路徑,但這一個 constructor 並沒有出現在 core framework 裡.你就得參考使用 FileStream 來寫資料到檔案裡.現在 .net core 還是 1.0 版,因此我相信在未來更新的版本裡它的 api 完整度一定會非常接近 .net full framework.有關 core framework api,可參考 http://dotnet.github.io/api/

2. CoreCLR,這也是核心版的 CLR.在 CLR 裡面最重要的兩個功能是 JIT 與 garbage collection,而在 CoreCLR 裡有屬於自己的 JIT 和 garbage collection.我不知道 CoreCLR 的 JIT 和 garbage collection 與 full framework 的有什麼差別,所以在這就無法提供太多的細節.

3. CoreRT,這是 core runtime,這部份聽起來好像有點怪怪的,也許你會覺得 coreclr 和 corert 有什麼差別,很抱歉,這部份的細節我也不知道,但我知道的是 corert 裡有 native language 轉換器,所以透過它,你可以把你的 c#/vb 等的程式碼變成 native code,也就是 machine code,而不是 IL code.所以有了它,你的 .net 程式將有機會變成無敵.^_^ 因為 native code 在運行時並不需要 framework/CLR 的存在,因為所有相關的元件也都編譯成 native code,所以你拿到的 native program 也會變的比較肥.

4. CLI,這是 command line interface,是 .net core 裡面所提供的工具鏈讓你可以透過指令的方式來操作 .net core.比如執行 dotnet new 來建立專案,執行 dotnet build 來進行編譯,執行 dotnet publish 來進行佈署程式,執行 dotnet test 來進行程式測試.甚至你可以為了某個目的來自行開發相關的工具鏈.比如,entity framework 將以前所有的指令移轉過來,所以你可以透過執行 dotnet ef dbconext list 來列出程式碼裡的 dbcontext,也可以執行 dotnet ef migrations 來做 ef migrations 的功能.

.Net Platform Standard

這對 ASP.NET Core 來說是個新東西,由於 ASP.NET Core 底層是 .net core 來執行,所以也就得遵守 .net core 所定義的平台標準.平台標準的意思就是讓應用程式可以指定要用什麼樣子的 api 來使用,所以 .net platform standard 基本上就像是一個菜單那般,而這份菜單上說明了使用的是那一個版本的元件.需要這樣做的原因是因為 .net core 要支援的平台其實很多,所以必須要製定出一個標準出來才能讓應用程式知道有什麼樣的 api 可以使用.舉例來說,如果你今天開發了一個應用程式的元件,你要讓這個元件可以用在 windows 8 與 windows phone 的話,那麼這個元件只能用一些 windows 8 和 windows phone 共同提供的 api 才行.其實這就是 .net portable class library 的概念.在 .net core 的世界裡便把這樣的概念定義的更明確以方便開發者知道在寫跨平台元件時要怎麼處理不同平台 api 之間的問題.所以,你可以把 .net platform standard 想成像物件導向裡的 interface 一樣,每個平台標準都定義好了 interface,所以你就知道你的應用程式在用這份 interface  時有什麼樣的 api 可以使用了.而目前定義了那些平台標準呢 ?  請參考這份文件 https://github.com/dotnet/corefx/blob/master/Documentation/architecture/net-platform-standard.md

source: https://github.com/dotnet/corefx/blob/master/Documentation/architecture/net-platform-standard.md

在上面的表格中,你可以看到 .net platform standard 一共定義了 1.0 到 1.5 ,一共六種標準.每一個標準代表了下面平台裡所代表的版本.例如,如果你的應用程式定義了要使用 .net platform standard 1.5 的話,那就表示當你使用 .net core 平台時,採用的是 .net core 1.0 版,或當你使用 .net framework 時,採用的是 .net 4.6.2 版.因此,這樣你在撰寫應用程式的時候你就知道你可以用什麼樣的 api 了.

若使用同一個平台的話,則低版本應相容於高版本.比如你的應用程式定義了要使用 .net standard 1.5,那表示你可以使用 .net framework 4.6.2 版的 api ,這也代表你也可以使用 .net framework 4.6.1 版的 api (定義在 .net standard 1.4).所以同一個平台時,低版本應相容於高版本.

source: https://github.com/dotnet/corefx/blob/master/Documentation/architecture/net-platform-standard.md

從上面的表格你也可以看到使用多個平台組合時,你所需要採用的 .net standard 版本.

每個平台在 .net standard 裡面都有定義一個縮寫名稱,同樣地也可以在相同那份文件裡找到資料.比如 .net framework 4.6 的縮寫是 net46,而 Xamarin iOS 的縮寫是 xamarinios.縮寫的名稱會需要用在 project.json 的 framework 定義中.這也使得當我們執行 dotnet restore 時,nuget 才會知道當下載某一個元件時需要下載那個平台的版本.舉個例子如下

"frameworks": {
    "netcoreapp1.0": {
         "imports":["portable-net45+win8"]
    }
}

這是在 project.json 裡面有關 framework 的定義.你可以看到這份應用程式說明了它將使用的是 netcoreapp1.0 的這份平台標準.從上面的內容中,你可以找出表格看到 netcoreapp1.0 也就是 .net standard 1.5 下所定義的平台.而 imports 裡面所寫的定義是為了向以前版本相容,因為有些舊名稱在 .net standard 的定義裡並不存在,所以暫時留了一個向前版本相容的空間.這份文件 ( https://github.com/dotnet/corefx/blob/master/Documentation/architecture/net-platform-standard.md) 也提到了那些縮寫名稱已不在使用.比如,以前 beta 到 RC1 幾乎都是寫 dnxcore50/aspnet50 在 project.json 的 framework 區塊裡,而這些名稱將不再使用了.

透過這樣的平台定義,你可以了解到當你為你的應用程式定義了越高版本的平台標準時,這代表了你可以使用越多的 api,同時也代表了跨平台的能力也相對地越低.這裡指的跨平台不限定是跨作業系統,windows 8 到 windows phone 到 universal windows app 也是屬於跨平台的意思.

Shared Runtime

前面的內容在 beta 版時已經有相似的東西,所以對使用過 beta 版的使用者來說並不是一個完全新的事情.而 shared runtime 這個應該就是新的了.

在 project.json 裡面也可以定義 runtimes,而 runtimes 定義代表你的應用程式會在作業系統裡面去找 runtime 來執行.這是什麼意思呢 ? 舉個例子來看比較快,

"runtimes": {
    "win7-x64": {}
}

在你的 project.json 中如果有以上這份定義,則代表你的程式執行時需要 win7-x64 版本的 runtime,你已經為你的應用程式指定了要這一份 runtime.所以,當你在做 dotnet publish 的時候,除了應用程式本身與相依的元件會產生出來以外,win7-x64 所有的 runtime 元件也會一併產生.因此,在你的 publish 輸出目錄裡,你除了看到應用程式本身以及相關元件外,你還會看到 runtimes 目錄,而裡面裝的就是 win7-x64 的 runtime 元件.

如果在你的 project.json 沒有定義像上面那樣的 runtimes 內容時,則當你在做 dotnet publish 時,.net core 就不知道你需要的 runtime 是那一個,因此它會在 publish 輸出目錄下獨立建一個目錄,然後把所有 runtimes 元件依作業系統名稱的目錄依序放好,因此在輸出目錄裡,除了你的程式及相關元件以外,還會有這一堆 runtimes 元件,但這並不代表你得把所有的內容帶走才能執行你的程式.放在 runtimes 目錄的那些 runtimes 程式可以不用跟著你的程式帶走,只要你的目地電腦上已安裝了 .net core,在這台電腦上執行此程式時,此程式會自動去依照環境變數的內容來找到該作業系統所需的 runtimes 元件.所以,你只要帶走你的程式及相關元件即可.檔案數量與容量就會小很多,這就是 shared runtime 的精神.但有一點千萬別誤會了,如果你已經指定了 runtime,則產生出來的程式就會依該作業系統不同而建立.比如,你指定 runtime 是 win7-x64 時,在編譯後你的程式將會是可執行檔的形式存在,讓你可以直接執行該檔案.但若你沒指定 runtime 時,在編譯後你的程式將以元件的形式存在,你只能用 dotnet.exe 來啟動你的程式.

希望以上的內容對於你在使用 ASP.NET Core RC2 版本與 .Net Core 時能有幫助.