.NET Core – The Beginning

隨著Microsoft擁抱開源的口號,.NET Framework也正式登上了其他平台,其中包含了Linux與OSX,與以往的3rd Party Mono不同,新的.NET Core具備官方的支援及完整的Roadmap。

為了登上其他舞台,.NET Core可以說從根源開始重新打造,在早期版本中Linux、OSX上仍是使用Mono Runtime,隨著時間的推移,現在的1.1 RTM中Mono的身影已經完全消失了,取而代之的是Native Platform Runtime,這代表著.NET平台新時代的來臨,也意味著Microsoft正走在一個Java曾走過的道路,Cross Platform、Write once,Run Anywhere。

 

第一個程式

 

  在開始寫程式前,你得先安裝.NET Core,這不難,照著網頁上寫的步驟做就可以了。

https://www.microsoft.com/net/core#windowsvs2015

本文使用Visual Studio 2015做為開發工具,當然,你也可以使用跨平台、更簡便的Visual Studio Code,同樣可以達到目的。

  準備好開發工具後,就可以透過Visual Studio 2015來建立第一個.NET Core應用程式,這裡使用Console Application,因為命令列式的應用程式可以讓我們以最快速、最簡單的步驟直達核心。

按下OK後就會看到熟悉的C#程式碼,編譯後能夠立刻執行。

如圖所示,你會發現這與傳統的.NET Console Application有兩個地方不同,第一個是.NET Core透過dotnet.exe來執行程式,如下圖所示。

第二個是編譯後的是一個DLL檔案,而不是傳統的EXE檔。

這其實有個目的,叫做Binary Sharing,也就是說你可以把這個DLL放到各種平台上執行而不需重新編譯,跟以往Java宣稱的Write Once、Run Anywhere是同樣道理。

在.NET Core中,除了編譯後的DLL檔之外,還有幾個很重要的JSON檔案:,.runtimeconfig.json這個檔案中描述了這個DLL是使用哪一種Runtime Framework,.NET Core可以使用不同的Runtime Framework,例如.NET 4.5.2、4.6、.NET Core 1.0、.NET Core 1.1等等,deps.json則描述了這個應用程式需要的Librarys,例如EntityFramework等等,這個檔案通常用於當只分發主程式而沒有分發其他Librarys,例如只複製DLL跟runtime.json到目的地,然後使用dotnet restore來下載其他相依的Librarys,這兩個json檔案的源頭是project.json。

{
  "version": "1.0.0-*",
  "buildOptions": {
    "emitEntryPoint": true
  },

  "dependencies": {
    "Microsoft.NETCore.App": {
      "type": "platform",
      "version": "1.1.0"
    }
  },

  "frameworks": {
    "netcoreapp1.0": {
      "imports": "dnxcore50"
    }
  }
}

注意,runtime.json檔案必須存在,否則這個應用程式無法執行。

 

 

Binary Sharing?

 

  談到跨平台,Binary Sharing就是一個很重要的議題,通常我們希望編譯後的結果可以拿到不同的平台上執行,而不是需要在不同平台上在編譯過後才能執行,.NET Core的應用程式具備了Binary Sharing特點,這裡我們用Cent OS為例(你必須事先在上面安裝好.NET Core)。

https://www.microsoft.com/net/core#linuxcentos

 

透過SSH或是FTP將編譯好的DLL及json檔案複製到Cent OS機器上,如你所見,同樣的指令,同一個DLL,不須重新編譯就可以執行,這就是Binary Sharing。

目前Visual Studio 2015的Template會指定.NET Core 1.0.1版本,所以如果Linux上的是最新的1.1.0版本,你會看到以下的訊息。

解法很簡單,就是在project.json中指定1.1.0即可。

 

project.json

{
  "version": "1.0.0-*",
  "buildOptions": {
    "emitEntryPoint": true
  },

  "dependencies": {
    "Microsoft.NETCore.App": {
      "type": "platform",
      "version": "1.1.0"
    }
  },

  "frameworks": {
    "netcoreapp1.0": {
      "imports": "dnxcore50"
    }
  }
}

 

自然,Binary Sharing同樣適用於Class Library。

 

 

Multi Framework Support

 

  .NET Core可以支援多個Framework,這意思是你可以用.NET Core方式來撰寫應用程式,然後在Windows上用.Net Framework 4.5作為Runtime來執行,而不需安裝.NET Core,這可以透過修改project.json來完成。

{
  "version": "1.0.0-*",
  "buildOptions": {
    "emitEntryPoint": true
  },
  "dependencies": {},

  "frameworks": {
    "netcoreapp1.0": {
      "imports": "dnxcore50",
      "dependencies": {
        "Microsoft.NETCore.App": {
          "type": "platform",
          "version": "1.1.0"
        }
      }
    },
    "net452": {}
  }
}

修改完後重新編譯,會發現輸出多了一組net452目錄。

在裡面可以看到熟悉的EXE檔。

這時內建的條件式編譯可以協助你針對不同Runtime調整行為,例如在完整的.NET Framework中使用Registry來儲存設定,在.NET Core使用本地的json檔之類的。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace SimpleConsoleApp1
{
    public class Program
    {
        public static void Main(string[] args)
        {
#if NET452
            Console.WriteLine("Hello .NET Framework");
#else
            Console.WriteLine("Hello .NET Core");
#endif
            Console.ReadLine();
        }
    }
}

以下是使用.NET Framework的情況。

接著是.Net Core的情況。

很有趣是吧?

 

Self-Contained

 

  除了使用Binary Sharing技術來分發應用程式之外,你也可以透過dotnet publish指令來打包Self-Contained應用程式,這意思是應用程式將內含.NET Core及相依的Librarys,目的平台不需要事先安裝.NET Core,簡單的說在Linux上,使用者看到的是執行檔而不是DLL,要完成這個,必須修改project.json檔案。

{
  "version": "1.0.0-*",
  "buildOptions": {
    "emitEntryPoint": true
  },
  "dependencies": {},

  "frameworks": {
    "net452": {},
    "netcoreapp1.0": {
      "imports": "dnxcore50",
      "dependencies": {
        "Microsoft.NETCore.App": {
          "version": "1.1.0"
        }
      }
    }
  },
  "runtimes": {
    "win7-x64": {},
    "centos.7-x64": {}
  }
}

要特別注意的是我們移除了netcoreapp1.0中的platform區段,否則無法編譯出self-contained的檔案,最後下達以下命令即可產生CentOS上的版本。

dotnet publish -c release -r centos.7-x64

最後把整個publish目錄的內容複製到CentOS上就可以執行(注意,你必須透過chmod來調整SimpleConsoleApp檔案為可執行檔案)。

 

 

Writing on WindowsRunning on LinuxDebug on Windows

 

  當應用程式需要執行在Windows以外的平台時,使用Docker來除錯是最簡單的的方式,但有時真實環境並非是Docker,而你又需要針對真實環境除錯時,這時就需要費點功夫了。首先必須要修改project.json,讓編譯器產生portable的pdb資訊。

{
  "version": "1.0.0-*",
  "buildOptions": {
    "emitEntryPoint": true,
    "debugType": "portable"
  },
  "dependencies": {},

  "frameworks": {
    "netcoreapp1.0": {
      "imports": "dnxcore50",
      "dependencies": {
        "Microsoft.NETCore.App": {
          "version": "1.1.0"
        }
      }
    }
  },
  "runtimes": {
    "win7-x64": {},
    "centos.7-x64": {}
  }
}

接著設定專案的輸出目錄為共用。

通常設定存取等級為everyone是最簡便的方式,然後到目標機器上安裝clsdbg套件。

curl -sSL https://raw.githubusercontent.com/Microsoft/MIEngine/getclrdbg-release/scripts/GetClrDbg.sh | bash /dev/stdin vs2015u2 ~/clrdbg

完成後home的目錄結構會變成類似下面這樣。

接著要在目標機器上mount shared folder。

最後需要一個.xml檔案,用來描述Windows如何透過SSH連結到目標LINUX機器。

Lanuchdebug.xml

<?xml version="1.0" encoding="utf-8" ?>
<PipeLaunchOptions xmlns="http://schemas.microsoft.com/vstudio/MDDDebuggerOptions/2014"
  PipePath="d:\develop\putty\plink.exe" PipeArguments="192.168.1.94 -l <user> -pw <password> -batch -t ~/clrdbg/clrdbg --interpreter=mi"
  TargetArchitecture="x64" MIMode="clrdbg" ExePath="dotnet" WorkingDirectory="~/myapp" ExeArguments="SimpleConsoleApp1.dll">
</PipeLaunchOptions>

完成準備後,就可以在Visual Studio 2015的Command Window中下達以下指令進入Debug模式。

Debug.MIDebugLaunch /Executable:dotnet /OptionsFile:d:\temp1\lanuchdebug.xml

順利的話可以停在設立的中斷點。

下次有機會我們再來聊聊.NET Core Hosting,這也很有趣。