.NET Magazine國際中文電子雜誌
作 者:許薰尹
審 稿:張智凱
文章編號: N200622001
出刊日期: 2020/6/10
一旦應用程式設計完成了,我們需要將它們部署到其它機器上運行,.NET Core提供了幾種部署方式,包含「與 Framework 相依的部署(Framework-dependent deployment,FDD)」、「自封式部署(Self-contained deployment,SCD)」。你可以利用Visual Studio 2019開發工具,或是利用CLI命令來打包發行設計好的.NET Core應用程式,在這篇文章中,我們將來了解一下使用Visual Studio 2019來進行發行與部署。
建立測試專案
讓我們先啟動Visual Studio 2019開發環境,建立測試專案。從Visual Studio開發工具「File」-「New」-「Project」項目,在「Create a New Project」對話盒中,選取「C#」程式語言,選取「Console App(NET Core)」,請參考下圖所示:

圖 1:建立「Console App(NET Core)」。
在「Configure your new project」視窗中設定專案名稱為「ConsoleApp1」,設定專案存放路徑,然後按下「Create」鍵,請參考下圖所示:

圖 2:「Configure your new project」視窗。
預設專案中包含一個「Program.cs」檔案,其中包含程式碼印出「Hello World!」訊息到主控台:
Program.cs
using System;
namespace ConsoleApp1 {
class Program {
static void Main( string[] args ) {
Console.WriteLine( "Hello World!" );
}
}
}
編譯這個專案,選取Visual Studio開發工具「Build」-「Build Solution」項目編譯目前的專案,確認程式碼能正確編譯,然後按CTRL+F5執行程式,執行結果參考如下圖所示:

圖 3:主控台應用程式。
接著讓我們使用Visual Studio 2019發行專案。
使用Visual Studio 2019發行專案
使用Visual Studio 發行.NET Core專案的方式又細分為以下幾種:
· 與 Framework 相依的部署(Framework-dependent deployment)。
· 有協力廠商相依性的 Framework 相依部署(Framework-dependent deployment)。
· 自封式部署(Self-contained deployment)。
· 有協力廠商相依性的自封式部署(Self-contained deployment)。
與 Framework 相依的部署(Framework-dependent deployment)
就名稱所暗示,「與 Framework 相依的部署(Framework-dependent deployment)」將依賴目的電腦需要事先安裝好.NET Core runtime,專案發行的結果只需要包含你的應用程式相關程式碼。如此的好處是.NET Core runtime可以讓多個不同的應用程式共享。
從「Solution Explorer」視窗,「ConsoleApp1」專案名稱上方按滑鼠右鍵,從快捷選單選擇「Publish」選項,請參考下圖所示:

圖 4:發行專案。
在「Pick a publish target」視窗中,選取發行到「Folder」,然後點選「Create Profile」按鈕
,請參考下圖所示:

圖 5:發行到資料夾。
在「Publish」發行視窗中設定以下項目:
· 「Target Locaiton」 :發行結果要輸出的資料夾。
· 「Configuration」:使用「Release」組態模式來發行。
· 「Target Framework」:設定「目標Framework」為「.netcoreapp 3.1」。
· 「Target Runtime」:設定為「Portable」,發行結果可運行在相容的機器上。例如.NET Core 2.0之後將Linux視為一個單獨的作業系統,編譯成「Portable」可適用於所有Linux環境中。
專案預設部署模式為「與 Framework 相依的部署(Framework-dependent deployment)」,我們直接點選「Publish」按鈕進行發行,請參考下圖所示:

圖 6:發行「Portable」。
完成後,透過檔案總管檢視「publish」資料夾,得到以下檔案清單,請參考下圖所示:

圖 7:「publish」資料夾。
「ConsoleApp1.pdb」檔案為程式資料庫(Program Database),包含應用程式的除錯相關資訊,協助除錯應用程式的例外錯誤。若無除錯需求,這個檔案可以不必部署到目的地電腦中。
發行的資料夾內會產生一個平台專屬的執行檔(platform-specific executable,.exe):「ConsoleApo1.exe」,也稱之為平台相依執行檔(framework-dependent executable),此執行檔(*.exe)不能夠跨平台,專屬於特定作業系統與CPU架構,「與 Framework 相依的部署(Framework-dependent deployment)」方式可以不需要這個執行檔(.exe),後續可以透過「dotnet.exe」來執行跨平臺二進位(cross-platform binary)檔(即附檔名為「*.dll」的檔案),這種部署方式是跨平台的。
跨平臺二進位(cross-platform binary)檔預設使用專案名稱命名,例如我們專案名稱為「ConsoleApp1」,產生的跨平臺二進位(cross-platform binary)檔名便為「ConsoleApp1.dll」。跨平臺二進位(cross-platform binary)檔可以在任何有安裝特定版本的Target Framework上執行(例如本文範例的「netcoreapp3.1」),若目地電腦沒有安裝此版本的.NET Runtime,則會找更新的版本來執行。
發行的結果中包含一個「*.deps.json」檔案,這個檔案記錄了相依組件的清單:
ConsoleApp1.deps.json
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v3.1",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v3.1": {
"ConsoleApp1/1.0.0": {
"runtime": {
"ConsoleApp1.dll": {}
}
}
}
},
"libraries": {
"ConsoleApp1/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}
發行的結果中包含一個「ConsoleApp1.runtimeconfig.json」檔案,指定要使用共用的Framework :「Microsoft.NETCore.App」來執行程式:
ConsoleApp1.runtimeconfig.json
{
"runtimeOptions": {
"tfm": "netcoreapp3.1",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "3.1.0"
}
}
}
好了,現在你只需要將「publish」資料夾中的檔案複製到目地電腦,目的地電腦要事先裝好.NET Core Runtime,以Windows作業系統為例,直接執行ConsoleApp1.exe平台專屬的執行檔(platform-specific executable),請參考下圖所示:

圖 8:執行ConsoleApp1.exe平台專屬的執行檔(platform-specific executable)。
或只是使用「dotnet」來執行跨平臺二進位(cross-platform binary)檔,請參考下圖所示:

圖 9:使用「dotnet」執行跨平臺二進位(cross-platform binary)檔。
設定執行階段識別碼(Runtime Identifier ,RID)
不管使用「與 Framework 相依的部署(Framework-dependent deployment)」或後面要談的「自封式部署(Self-contained deployment)」,發行的資料夾內會產生一個平台專屬的執行檔(.exe),你可以在發行時指定執行階段識別碼(Runtime Identifier ,RID),來指定想要使用的平台,例如「win-x64」、「win-x86」、「linux-x64」等等,完整的執行階段識別碼清單可以參閱微軟官方網站:「https://docs.microsoft.com/zh-tw/dotnet/core/rid-catalog」。
舉例來說,若要設定執行階段識別碼(Runtime Identifier ,RID),在「Publish」視窗中,選取「Edit」項目,請參考下圖所示:

圖 10:設定執行階段識別碼(Runtime Identifier ,RID)。
在「Profile Settings」視窗中設定「Target Runtime」,請參考下圖所示:

圖 11:設定「Target Runtime」指定RID。
自封式部署(Self-contained deployment)
「自封式部署(Self-contained deployment)」會將你應用程式程式碼、.NET Core程式庫、.NET Core Runtime打包在一起,目地電腦不必事先裝.NET Core。如此的好處是一台機器上,可以有多個程式,在不同版本的.NET Core環境中並行運行。
讓我們試著將「Deployment Mode」設定為「Self-contained」,再進行主控台應用程式的發行,請參考下圖所示:

圖 12:自封式部署(Self-contained deployment)。
檢視「publish」資料夾,其中共有226個檔案,除了專案程式編譯完的組件之外,還包含.NET Core Runtime等共用程式庫,這些檔案可以直接複製到沒有安裝.NET Core Runtime的Windows機器上執行,請參考下圖所示:

圖 13:自封式部署(Self-contained deployment)。
有協力廠商相依性的 Framework 相依部署(Framework-dependent deployment)
若應用程式與協力廠商的組件有相依性,那麼發行的結果有哪些不同呢? 讓我們修改一下主控台應用程式的程式碼,使用很流行的Json.NET函式庫來進行物件序列化與還原序列化動作。先使用Nuget套件管理員下載Json.NET函式庫。選取「Solution Explorer」視窗-「ConsoleApp1」專案。從Visual Studio開發工具「Tools」-「NuGet Package Manager」-「Package Manager Console」開啟「Package Manager Console」視窗,然後在提示字元中輸入install-package指令:
install-package Newtonsoft.Json
執行結果請參考下圖所示:

圖 14:使用Nuget套件管理員安裝Json.NET函式庫。
檢視專案檔案包含「PackageReference」項目,描述了相依性,請參考以下程式碼:
ConsoleApp1.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
</Project>
修改「Program.cs」檔案,加入以下程式碼,利用「JsonConvert」類別序列化「List<Book>」集合,並就序列化的結果,再做還原序列化:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
namespace ConsoleApp1 {
public class Book {
public int Id { get; set; }
public string Title { get; set; }
public int Price { get; set; }
public DateTime PublishDate { get; set; }
public bool InStock { get; set; }
public string Description { get; set; }
public Category? Category { get; set; }
}
public enum Category {
Arts, Business, Commics, Cooking, Computers, History, Literature, Sports, Travel
}
class Program {
static void Main( string[] args ) {
List<Book> _books = new List<Book>() {
new Book() {
Id = 1 ,
Title = " Essential Programming Language " ,
Price = 250 ,
PublishDate = new DateTime( 2019 ,1,2 ) ,
InStock = true ,
Description = "Essential Programming Language " ,
Category = Category.Computers
},
new Book() {
Id = 2 ,
Title = " Telling Arts " ,
Price = 245 ,
PublishDate = new DateTime( 2019 , 4 , 15 ) ,
InStock = true ,
Description = " Telling Arts " ,
Category = Category.Arts
},
new Book() {
Id = 3 ,
Title = " Marvel " ,
Price = 150 ,
PublishDate = new DateTime( 2019 , 2, 21 ) ,
InStock = true ,
Description = " Marvel " ,
Category = Category.Commics
},
new Book() {
Id = 4 ,
Title = " The Beauty of Cook" ,
Price = 450 ,
PublishDate = new DateTime( 2019 ,12,2 ) ,
InStock = true ,
Description = " The Beauty of Cook " ,
Category = Category.Cooking
}
};
string jsonText = JsonConvert.SerializeObject( _books , Formatting.Indented);
Console.WriteLine( jsonText );
Console.WriteLine();
List<Book> jsonObjList = JsonConvert.DeserializeObject<List<Book>>( jsonText );
Console.WriteLine( "Book list : " );
foreach ( var item in jsonObjList ) {
Console.WriteLine( $" {item.Id} , {item.Title} , {item.Price} , {item.InStock} , {item.Category}" );
}
}
}
}
選取Visual Studio開發工具「Build」-「Build Solution」項目編譯目前的專案,確認程式碼能正確編譯。在Visual Studio開發工具,按CTRL+F5執行,執行的結果請參考下圖所示:

圖 15:使用「JsonConvert」類別序列化與還原序列化。
我們先試試「有協力廠商相依性的 Framework 相依部署(Framework-dependent deployment)」,請參考下圖在「Profile Settings」視窗,進行以下設定,再進行發行:

圖 16:有協力廠商相依性的 Framework 相依部署(Framework-dependent deployment)。
發行之後,檢視「publish」資料夾,Visual Studio會自動將專案相依的「Newtonsoft.json.dll」加到資料夾之中,請參考下圖所示:

圖 17:有協力廠商相依性的 Framework 相依部署(Framework-dependent deployment)。
此外檢視「ConsoleApp1.deps.json」檔案,也會紀錄應用程式使用到的相依組件「Newtonsoft.Json」,請參考以下程式碼:
ConsoleApp1.deps.json
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v3.1/win-x64",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v3.1": {},
".NETCoreApp,Version=v3.1/win-x64": {
"ConsoleApp1/1.0.0": {
"dependencies": {
"Newtonsoft.Json": "12.0.3"
},
"runtime": {
"ConsoleApp1.dll": {}
}
},
"Newtonsoft.Json/12.0.3": {
"runtime": {
"lib/netstandard2.0/Newtonsoft.Json.dll": {
"assemblyVersion": "12.0.0.0",
"fileVersion": "12.0.3.23909"
}
}
}
}
},
"libraries": {
"ConsoleApp1/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Newtonsoft.Json/12.0.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-6mgjfnRB4jKMlzHSl+VD+oUc1IebOZabkbyWj2RiTgWwYPPuaK1H97G1sHqGwPlS5npiF5Q0OrxN1wni2n5QWg==",
"path": "newtonsoft.json/12.0.3",
"hashPath": "newtonsoft.json.12.0.3.nupkg.sha512"
}
}
}
有協力廠商相依性的自封式部署(Self-contained deployment)
最後試試「有協力廠商相依性的自封式部署(Self-contained deployment)」,請參考下圖在「Profile Settings」視窗,進行以下設定,再進行發行:

圖 18:「有協力廠商相依性的自封式部署(Self-contained deployment)」。
發行之後,檢視「publish」資料夾,Visual Studio同樣會自動將專案相依的「Newtonsoft.json.dll」加到資料夾之中,請參考下圖所示:

圖 19:「有協力廠商相依性的自封式部署(Self-contained deployment)」。
產生單一檔案 (Produce a single file)
.NET Core 3.x版新增一個發行選項,請參考下圖所示,能夠將發行結果,包含你的應用程式、相依程式,與.NET Core runtime等等全部打包成一個可執行檔案,只要在「Profile Settings」視窗,勾選「Produce a single file」,請參考下圖所示:

圖 20:產生單一檔案 (Produce a single file)。
發行完,檢視「publish」資料夾,其中只包含兩個檔案,Visual Studio 2019會將相依檔案全部打包在同一個單一執行檔之中,檔案的大小為「68162KB」,請參考下圖所示:

圖 21:產生單一檔案 (Produce a single file)- 「Self - contained」。
讓我們改設定為「Framework Dependent」再進行發行,請參考下圖所示:

圖 22:產生單一檔案 (Produce a single file)- 「Framework Dependent」。
發行後,檔案的大小為「852KB」,比設定為「Self - contained 」發行要小很多,請參考下圖所示:

圖 23產生單一檔案 (Produce a single file)- 「Framework Dependent」。
這個執行檔內將含執行應用程式所有相依程式,第一次執行時會自我解壓縮到暫存資料夾,下次再執行時,就不需要再進行一次解壓縮的動作。
修剪未使用的組件(Trim unused assemblies)
自封式部署(Self-contained deployment)還有一個額外的「修剪未使用的組件(Trim unused assemblies」功能,能將未使用到的程式移出輸出的組件。在發行時勾選「Trim unused assemblies」,請參考下圖所示:

圖 24:修剪未使用的組件(Trim unused assemblies)。
發行後,檢視檔案大小比沒有勾選「修剪未使用的組件(Trim unused assemblies)」選項的發行方式少了一半,約「35,935KB」,請參考下圖所示:

圖 25:修剪未使用的組件(Trim unused assemblies)。
啟用ReadyToRun編譯
.NET Core 3.x版之後,多了一個「啟用ReadyToRun編譯(Enable Ready ToRun compilication)」選項搭配發行使用,可以將程式編譯成ReadyToRun (R2R) 格式。勾選「啟用ReadyToRun編譯(Enable Ready ToRun compilication)」選項後發行的結果將包含原生程式碼(Native Code),可以改善應用程式啟動的時間。

圖 26:啟用ReadyToRun編譯(Ready to Run)。
勾選「啟用ReadyToRun編譯(Enable Ready ToRun compilication)」選項後發行結果的檔案較上個設定大一些,請參考下圖所示,這是因為其中同時包含了原生程式與中介(IL)程式。

圖 26:啟用ReadyToRun編譯(Ready to Run)。
總結
「與 Framework 相依的部署(Framework-dependent deployment)」的特色在於:
- 部署的檔案小,只有你的應用程式與相依組件需要複製到目地電腦。NET Core Runtime要事先安裝到目地電腦,可以讓多個應用程式共享。
- 程式可以跨平台。
- 目的電腦的.NET Runtime更新時(主要、次要版本)會自動使用新版的.NET Runtime來執行。
「自封式部署(Self-contained deployment)」的特色在於:
- 可以控制應用程式使用的.NET Core版本。多個.NET Core版本可以並存在一台機器上。
- 部署的檔案較大。