發行與部署.NET Core應用程式

by vivid 10. 六月 2020 03:19

.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)」,請參考下圖所示:

clip_image002

圖 1:建立「Console App(NET Core)」。

在「Configure your new project」視窗中設定專案名稱為「ConsoleApp1」,設定專案存放路徑,然後按下「Create」鍵,請參考下圖所示:

clip_image004

圖 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執行程式,執行結果參考如下圖所示:

clip_image006

圖 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」選項,請參考下圖所示:

clip_image008

圖 4:發行專案。

在「Pick a publish target」視窗中,選取發行到「Folder」,然後點選「Create Profile」按鈕

,請參考下圖所示:

clip_image010

圖 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」按鈕進行發行,請參考下圖所示:

clip_image012

圖 6:發行「Portable」。

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

clip_image014

圖 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),請參考下圖所示:

clip_image016

圖 8:執行ConsoleApp1.exe平台專屬的執行檔(platform-specific executable)。

或只是使用「dotnet」來執行跨平臺二進位(cross-platform binary)檔,請參考下圖所示:

clip_image018

圖 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」項目,請參考下圖所示:

clip_image020

圖 10:設定執行階段識別碼(Runtime Identifier ,RID)。

在「Profile Settings」視窗中設定「Target Runtime」,請參考下圖所示:

clip_image022

圖 11:設定「Target Runtime」指定RID。

自封式部署(Self-contained deployment)

「自封式部署(Self-contained deployment)」會將你應用程式程式碼、.NET Core程式庫、.NET Core Runtime打包在一起,目地電腦不必事先裝.NET Core。如此的好處是一台機器上,可以有多個程式,在不同版本的.NET Core環境中並行運行。

讓我們試著將「Deployment Mode」設定為「Self-contained」,再進行主控台應用程式的發行,請參考下圖所示:

clip_image024

圖 12:自封式部署(Self-contained deployment)。

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

clip_image026

圖 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

執行結果請參考下圖所示:

clip_image028

圖 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執行,執行的結果請參考下圖所示:

clip_image030

圖 15:使用「JsonConvert」類別序列化與還原序列化。

我們先試試「有協力廠商相依性的 Framework 相依部署(Framework-dependent deployment)」,請參考下圖在「Profile Settings」視窗,進行以下設定,再進行發行:

clip_image032

圖 16:有協力廠商相依性的 Framework 相依部署(Framework-dependent deployment)。

發行之後,檢視「publish」資料夾,Visual Studio會自動將專案相依的「Newtonsoft.json.dll」加到資料夾之中,請參考下圖所示:

clip_image034

圖 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」視窗,進行以下設定,再進行發行:

clip_image036

圖 18:「有協力廠商相依性的自封式部署(Self-contained deployment)」。

發行之後,檢視「publish」資料夾,Visual Studio同樣會自動將專案相依的「Newtonsoft.json.dll」加到資料夾之中,請參考下圖所示:

clip_image038

圖 19:「有協力廠商相依性的自封式部署(Self-contained deployment)」。

產生單一檔案 (Produce a single file)

.NET Core 3.x版新增一個發行選項,請參考下圖所示,能夠將發行結果,包含你的應用程式、相依程式,與.NET Core runtime等等全部打包成一個可執行檔案,只要在「Profile Settings」視窗,勾選「Produce a single file」,請參考下圖所示:

clip_image040

圖 20:產生單一檔案 (Produce a single file)。

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

clip_image042

圖 21:產生單一檔案 (Produce a single file)- 「Self - contained」。

讓我們改設定為「Framework Dependent」再進行發行,請參考下圖所示:

clip_image044

圖 22:產生單一檔案 (Produce a single file)- 「Framework Dependent」。

發行後,檔案的大小為「852KB」,比設定為「Self - contained 」發行要小很多,請參考下圖所示:

clip_image046

圖 23產生單一檔案 (Produce a single file)- 「Framework Dependent」。

這個執行檔內將含執行應用程式所有相依程式,第一次執行時會自我解壓縮到暫存資料夾,下次再執行時,就不需要再進行一次解壓縮的動作。

修剪未使用的組件(Trim unused assemblies)

自封式部署(Self-contained deployment)還有一個額外的「修剪未使用的組件(Trim unused assemblies」功能,能將未使用到的程式移出輸出的組件。在發行時勾選「Trim unused assemblies」,請參考下圖所示:

clip_image048

圖 24:修剪未使用的組件(Trim unused assemblies)。

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

clip_image050

圖 25:修剪未使用的組件(Trim unused assemblies)。

啟用ReadyToRun編譯

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

clip_image052

圖 26:啟用ReadyToRun編譯(Ready to Run)。

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

clip_image054

圖 26:啟用ReadyToRun編譯(Ready to Run)。

總結

「與 Framework 相依的部署(Framework-dependent deployment)」的特色在於:

  • 部署的檔案小,只有你的應用程式與相依組件需要複製到目地電腦。NET Core Runtime要事先安裝到目地電腦,可以讓多個應用程式共享。
  • 程式可以跨平台。
  • 目的電腦的.NET Runtime更新時(主要、次要版本)會自動使用新版的.NET Runtime來執行。

「自封式部署(Self-contained deployment)」的特色在於:

  • 可以控制應用程式使用的.NET Core版本。多個.NET Core版本可以並存在一台機器上。
  • 部署的檔案較大。

Tags:

.NET Magazine國際中文電子雜誌 | .NET Core | C# | 許薰尹Vivid Hsu

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>

月分類Month List