Microsoft .NET 自 2002年發行 v1.0 以來,已經過了近 14 個年頭,在這 14 年裡面,.NET 日漸成熟並成為 Microsoft 的重要開發平台之一,只要是在 Windows 平台上的相關應用,幾乎都可以使用 .NET 以及所屬的 C# 及 VB 程式語言來開發,雖然它一直沒有真正的跨平台 (也可以說有,但只跨 Windows 生態圈的平台),不過 .NET 與 Visual Studio 的完美整合所產生的生產力,也是軟體產業無法否認的強大,Visual Studio 號稱地表最強開發工具是一點都不為過。
只是 .NET 的天生包袱終究使得它的可運用範圍一直被侷限於 Windows 生態圈,對另外一個生態圈 — Linux 與 Open Source 的生態圈而言,一直是微軟與 .NET 無法跨過的高牆。直到了微軟新任 CEO Satya Nadella 於 2013 年正式上任,並提出 Mobile First, Cloud First 的策略指導原則後,整個微軟幾乎動起來,試著想要推倒這一面高牆,而第一個產品就是 ASP.NET vNext,隨後又宣佈了 .NET Core 計畫,作為整體策略的第一步。
跨越鴻溝的努力
大家都知道 Microsoft Azure 是微軟重要的雲端運算產品線,Azure 的開放特性使得 Windows 平台與 Linux 平台的環境都可以建置在 Azure 平台,然而受限於 Windows 的生態圈,微軟總是在雲端技術的發展中落後,因為雲端技術的主流與先鋒都是由 Linux 的生態圈發起,這種例子不勝枚舉,像是 Docker 利用的是 Linux Container (LXC);Hadoop 是以 Java 開發並運行在 Linux 上;Apache Mesos 與 Spark 等管理與大數據分析平台也是在 Linux 上發展,相較之下,以 Windows 生態圈為主的微軟陣營總是在後頭追,移植受歡迎的軟體平台或套件到 Windows,因此上市時間總比其他陣營晚,這點在 Azure 與 Amazon AWS 的服務競爭中就可見端晲。所以即使 C# 語言的發展速度比 Java 快了幾個量級 (例如 C# 的 Lambda Expression 功能 Java 到 Java 8 才出現),但是發展卻不如 Java。
因此 2014 年開始,微軟終於跨出第一步,由 Satya Nadella 站上第一線向大家宣告 Microsoft Love Linux,自此開始,微軟許多的技術與應用開始排山倒海的往 Windows 以外的平台推進,應用面就屬 Office for iOS 與 Android 最廣為人知,微軟在 Windows 10 的發表會上也宣布數項計畫,讓 iOS 與 Android 的應用程式能直接跑在 Windows 10 平台等。開發工具的部份則是以 Visual Studio Code 作為先鋒,主力部隊將由 .NET Core 與其上的 Web 開發平台 ASP.NET Core 擔綱,以官方的角色將 .NET 推進到 Linux 與 Open Source 生態圈。即便是 Windows 平台上的 Visual Studio,微軟也納入了來自 Open Source 陣營的知名實作品,例如 bower、Gulp、Grunt、AngularJS 等,並加入了像 Docker Tools 這樣的工具,讓原本熟悉 Visual Studio 的開發人員能以最少的改變切入非 Windows 的環境。
但是,雖然微軟做了很多努力,開發人員還是有些本質上的差異需要習慣,這是因為 Windows 的生態圈重視的是 GUI (圖形化介面),但 Linux 生態圈大多數都是以 CLI (命令列介面) 為主,所以 .NET Core 與 ASP.NET Core 也將會著重在 CLI 上,只有在 Windows 上的 Visual Studio 還能保有多數的 GUI 介面,就連 Visual Studio Code 也偏重在 CLI。
.NET Core & ASP.NET Core
微軟最早啟動的跨平台的開發環境是 Project K,它是 ASP.NET Core 1 在專案初始時期的代號,在一開始的時候就提供了執行器 k.exe、版本管理工具 kvm.exe 以及套件封裝與管理工具 kpm.exe,當時就很明顯的要使用指令進行開發工作,它和原有的 .NET 執行環境也有很大的不同,它獨立於 .NET Framework 的執行期之外,稱為 KRE (K Runtime Environment),核心的載入器則稱為 KLR (K Language Runtime)。後來 Project K 被改名為 ASP.NET 5,其下的各個執行環境被改為 .NET 執行環境 (DotNet eXecution environment; DNX),此時作為跨平台核心的 .NET Core 也開始進行,其執行環境稱為 Core CLR,不論是 ASP.NET 5 或是 .NET Core 都是具有跨出 Windows 平台能力的執行環境,就連 Windows 10 的 Universal Windows Platform 的 .NET 平台使用的也是 Core CLR,搭配為 Windows 開發的 .NET Native 編譯器來強化執行效能。
(source: Immo Landwerth, .NET Core is Open Source)
.NET Core 是第一個由微軟官方所開發,具有跨出 Windows 平台能力的開發平台,作為下一代 .NET 開發的基石,同時它會和現有的 .NET Framework 並行,在 Windows 上可同時存有這兩種執行環境,而且 .NET Core 的程式可存取 .NET Framework 的類別庫以保有相容性 (當然跨出 Windows 後就不行了),.NET Core 本身的類別庫也重新設計,改為以套件散佈方式提供,也就是說,以 .NET Core 開發的應用程式不再需要傳統大包裝的 Framework Runtime,只需要執行前下載 Core CLR,並且在執行程式前先行還原套件,就可以執行,而且套件的版本與各應用程式可各自獨立,這可是一項重大的進步,以往或許寫個小程式還要帶大包 Framework 的景象未來將不再復見,同時套件化的管理也保有版本相容性,應用程式可視需要抓取所需的版本,而不用固定在一個大版。.NET Core 在不同的平台也可以編譯成原生碼,前面提到 Windows 是使用 .NET Native 編譯,Linux 與 Mac 則是使用 LLVM MSIL Compiler (LLILC) 進行編譯,.NET Core 的建造工具還可產生 C++ 程式碼,再使用 GCC 等編譯器編譯為原生碼。
至於 Web 端的開發平台,則非 ASP.NET 5 莫屬,但因為 ASP.NET 5 這個名稱很容易讓人誤會它是 ASP.NET 4.x 的升級版,但其實它是完全重寫的新版本,為了不讓外界誤解,微軟決定將它再改一次名字,即現在的名字 ASP.NET Core。它最大的特色就是揚棄了 System.Web.dll 這個 .NET Framework 最大的組件,當然少了 System.Web.dll 也等於宣告 Web Form 不會被移植到 ASP.NET Core,同時微軟也花了很多的心力將 System.Web.dll 內的類別進行重構與切割,分散到各個不同的組件,為 ASP.NET Core 的核心進行瘦身,以加快 ASP.NET Core 的執行效能。當然,ASP.NET Core 和 .NET Core 一樣,只需要取得或還原所相依的套件即可執行。而另一個重大的改變,是 ASP.NET Core 不再依賴 IIS,這也歸功於 System.Web.dll 的重構,ASP.NET Core 的執行環境由新開發的 Kestrel Server 負責,IIS 退回到 HTTP 的聆聽器的角色,微軟也特別為了這個需求開發了 IIS Platform Handler,以處理 HTTP 與執行環境之間的訊息轉送工作。ASP.NET Core 除了核心之外,它也帶來了 MVC 6 這個強悍的開發框架,以及其週邊的開發支援,如 ASP.NET Identity 3.0、SignalR 3.0 等新版本的開發框架。
取得開發環境
.NET Core 與 ASP.NET Core 在 RC1 的版本,其 CLI 命令列工具尚未合併,微軟已經計畫於 RC2 時將兩個環境的命令列合併,不過在此之前,還是要各自取得。.NET Core 可以取自其官方網站 http://dotnet.github.io/getting-started/,其工具只有一個:.NET CLI,執行檔名稱為 dotnet.exe,其下有相關參數可用,較常用的指令有:
功能 | 指令 |
在目錄下建立新專案 | dotnet new |
還原必要套件 | dotnet restore (第一次執行時都要跑一次) |
編譯專案 | dotnet compile |
執行專案 | dotnet run |
建置專案 | dotnet builddotnet build –native (使用RyuJIT編譯)dotnet build –native -cpp (編譯並使用C++程式產生器) |
封裝套件 | dotnet pack |
發行專案 | dotnet publish |
ASP.NET Core 的指令則是由 .NET 執行環境提供,分為幾個不同的指令:
- exe: 執行 ASP.NET Core 程式。
- exe: 管理 DNX 的版本,可安裝或指定要執行的 DNX Runtime 的版本。
- exe: 還原 ASP.NET Core 所需的套件,以及建造程式與封裝部署等。
較常用的指令有:
功能 | 指令 |
安裝特定 DNX 版本 | dnvm install |
升級版本到最新版並設為預設 | dnvm upgrade -r [clr|coreclr] -a [x86|x64] |
將指定版本設為預設 | dnvm use |
列出可用 DNX 版本 | dnvm list |
升級 dnvm 工具 | dnvm update-self |
建置專案 | dnu build |
封裝專案為套件 | dnu pack |
還原套件 | dnu restore |
列舉相依套件 | dnu list |
發行專案 | dnu publish |
清除套件快取 | dnu clear-http-cache |
執行專案 | dnx |
ASP.NET Core 不像 .NET Core 有 .NET CLI 可初始化預設專案,若是用 Windows 開發,可使用 Visual Studio 產生專案,但若是使用 Mac 或是 Linux 開發時,則建議使用 Yeoman ASP.NET 5 Project Generator,它可以在目錄下建立新的專案範本,再使用 Visual Studio Code 或是其他文字編輯工具編輯程式碼,完成後使用命令列工具編譯執行即可。
認識 project.json
不論是 ASP.NET Core 5 還是 .NET Core,其專案資料夾內都會包含一個 project.json 檔案,它記錄了這個專案所需要的相關設定,包含使用的 .NET Core/DNX 版本、其相依的套件資訊、特定平台的相依資訊、啟動參數與指令、啟動事件等,下列內容即是預設 ASP.NET Core 1 的 Web 應用程式專案的 project.json:
其中最重要的部份是 dependencies 區段,它定義了專案所相依的套件與其版本,在 Visual Studio 編輯時,Visual Studio 會自動掃瞄 NuGet 取得相似的套件名稱與版本。
若是使用 Visual Studio Code,也是一樣會出現提示:
當開發人員編修過 project.json 的 dependencies 時,Visual Studio 會主動呼叫 dnu restore 指令進行套件還原,此時就可以在 Visual Studio 的參考中看到套件的資訊。
由於 .NET Core 與 ASP.NET Core 可同時在 Windows 與非 Windows 的平台上執行,Windows 平台上是以 4.5.1 為基準,非 Windows 平台則是以 Core 5.0 為基準,若參考到了某一個平台不相容的套件,該套件上的圖示會出現驚嘆號,且編譯時會無法通過,若確定要鎖定在 Windows 上時,可將下方 frameworks 區段的 dnxcore50 移除,如此 dnu 就只會以 .NET 4.5.1 版本編譯。
frameworks 區段還有另一個好處,是可以將特定平台的組件參考加進來,編譯時 dnu 就會取用這些組件一起編譯。
commands 區段則定義了傳遞給 dnx 時的指令參數,不同的指令參數包含了要啟動的程式以及其必要參數。如下列,web 指令表示啟動 Microsoft.AspNet.Server.Kestrel (Kestrel Server),而 ef 表示啟動 EntityFramework.Commands 程式。
其他的 project.json 的區段,可參考 ASP.NET Core 團隊寫的 Project.json 結構參考:https://github.com/aspnet/Home/wiki/Project.json-file
前端資源管理
ASP.NET Core 的專案結構將後台資源與前台分開,前台的資源統一置於 wwwroot 資料夾下,應用程式若有什麼靜態的資源,可以放在這個資料夾內,部署時 dnu 會自動將這個資料夾內的資源配置到網站的根目錄下 (但若要讓 ASP.NET Core 應用程式能成功存取,還要加入 Microsoft.AspNet.StaticFiles 套件,稍後會提到)。
ASP.NET Core 也引入了前端套件管理服務 bower 與工作執行服務 Gulp/Grunt,在預設的 project.json 內就包含了發行前啟動前端套件程式的指令:
在 Visual Studio 裡面,針對 Gulp 與 Grunt 提供了工作執行器總管功能,可讓開發人員看到工作執行器執行的狀態;針對 bower.js 提供了相依套件管理,如同 NuGet 套件管理的操作模式,開發人員能輕鬆的管理前端的相依套件,預設的情況下, bower.js 下載的相依前端套件都會放在 wwwroot/lib 資料夾內。
後端功能管理
ASP.NET Core 的重大改變之一,就是開發人員必須使用程式碼來定義功能 (Feature),以往是在 Web.config 以及 IIS 上設定啟用或停用功能,在 ASP.NET Core 不再有 Web.config,也不再只能跑在 IIS 上,所以功能的啟用要由程式碼處理,只有程式碼啟用的功能才會啟用,沒有的就不會有作用,在 ASP.NET Core 專案範本內有個 Startup.cs,所有應用程式要啟用或停用的功能都要在這裡設定。以下的例子是預設專案範本的 Startup.cs 內容 (程式碼有修剪過)。
初次看到這段程式碼可能容易覺得適應不良,其實關鍵是在於 ASP.NET Core 採用來OWIN (Open Web Interface for .NET) 的架構設計,所有可掛於 ASP.NET Core 的功能組件都會實作一個對 IApplicationBuilder 介面的擴充方法,名稱基本上都是以 Use 開頭,像是要啟用靜態檔案讀取,就是使用 app.UseStaticFiles();,這個方法定義在 Microsoft.AspNet.StaticFiles 套件內,你必須要加入這個套件的參考,才可以在 Visual Studio 看到這個方法,其他像 app.UseIdentity();以及 app.UseMvc(); 都是類似的作法。只有用程式碼加入 (啟用) 的服務,才可以在程式中使用。
ASP.NET Core 本身也具備強大的 Dependency Injection 基礎建設,因此開發人員也可以在應用程式中的服務集合中加入自己的服務,例如:
同樣的,ASP.NET Core 的功能模組也擴充了 IServiceCollection 介面的功能,以簡化呼叫的語法與語意,例如 serivces.AddMvc(); 表示在服務中加入 MVC 的功能;services.AddIdentity(); 表示加入 ASP.NET Identity 的功能; services.AddEntityFramework(); 表示加入 Entity Framework 的功能等。
應用程式組態
Web.config 在 ASP.NET Core 中不再存在 (wwwroot 裡面的 Web.config 是為了要註冊 IIS Platform Handler),相關的應用程式設定都移到了 appsettings.json,同時也不再區分 appSettings 與 connectionStrings,例如:
組態檔也不是預設就能在程式中生效,一樣要使用程式碼將組態檔加入,才可以在程式中使用。Microsoft.Extensions.Configuration 定義了 ASP.NET Core 與 .NET Core的組態系統,並提供 JSON、INI、XML 等組態檔格式的支援。
總結
.NET Core 與 ASP.NET Core,這兩個主要的開發平台組成的 Core 家族,是微軟進軍非 Windows 生態圈的重要技術,它們都可以運行在 Linux 與 Mac,也可以包裝到 Docker 成為容器,.NET Core 讓 .NET 能被重新定義,而 ASP.NET Core 帶來許多改變,從正式脫離 System.Web.dll 開始,用程式碼定義功能、導入知名前端管理框架、不依賴 IIS 的環境等。.NET Core 與 ASP.NET Core 不但能適用於跨平台,也對運行在雲端環境與實作微服務 (Microservices) 有著相當大的潛能。
改變是需要習慣的,所以身為微軟陣營的開發人員,與其再繼續觀望,不妨就立刻開始習慣它吧,當你習慣了之後,你會發現在前面的道路是非常寬廣的。