原文連接: Deep-dive into .NET Core primitives: deps.json, runtimeconfig.json, and dll's
做者: Nate McMastergit
C#的編譯器能夠將cs文件轉換爲dll文件, 即程序集文件。程序集文件是一個便攜的可執行格式文件, 藉助.NET Core,它能夠運行在Windows, MacOS和Linux系統中。github
在Windows系統中, .NET Core的編譯器文件csc.dll存放在如下目錄中json
C:\Program Files\dotnet\sdk\[.NET Core 版本號]\Roslyn\bincore
筆者使用了2.1.400版本,因此編譯器存放目錄是C:\Program Files\dotnet\sdk\2.1.400\Roslyn\bincore緩存
.NET Core編譯器文件csc.dll
也是一個.NET Core應用程序,因此你可使用dotnet
命令直接執行編譯器app
C:\test>dotnet C:\Program Files\dotnet\sdk\2.1.400\Roslyn\bincore\csc.dll --help
下面咱們嘗試手動編譯一個cs文件。
首先咱們先建立一個Program.cs
文件,內容以下:框架
/* Program.cs */ class Program { static void Main(string[] args) => System.Console.WriteLine("Hello World!"); }
而後咱們使用命令行命令將其編譯編輯器
C:\test>dotnet "C:\Program Files\dotnet\sdk\2.1.400\Roslyn\bincore\csc.dll" -reference:"C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Runtime.dll" -reference:"C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Console.dll" -out:Program.dll Program.cs
"C:\Program Files\dotnet\sdk\2.1.400\Roslyn\bincore\csc.dll"
是編譯器所在的路徑-reference
參數表示編譯中須要引用的dll, 該參數能夠指定多個dll , 例子中咱們引用了System.Runtime.dll和System.Console.dll-out
參數表示編譯生成的dll路徑Program.cs
表示編譯的源文件地址Program.cs
編譯成功, Program.dll
生成完畢。ide
對於.NET Core應用程序來講runtimeconfig.json
是不可或缺的。它是用來配置運行時的。優化
若是缺乏了這個文件,運行dll文件的時候會產生如下異常。ui
C:\test>dotnet Program.dll A fatal error was encountered. The library 'hostpolicy.dll' required to execute the application was not found in '........'
這句話的意思是.NET Core缺乏指定組件來運行程序。
爲了解決這個問題,咱們能夠添加一個Program.runtimeconfig.json
, 其內容以下
{ "runtimeOptions": { "framework": { "name": "Microsoft.NETCore.App", "version": "2.1.2" } } }
這裏的配置dotnet命令將使用Microsoft.NETCore.App
做爲共享框架(Shared Framework)。當dotnet命令運行的時候,它會去runtimeconfig.json中讀取版本號,而後去C:\Program Files\dotnet\shared\[庫名]\[版本號]
目錄下,搜索對應的dll文件
如今咱們從新運行上面的命令,結果以下:
C:\test>dotnet Program.dll Hello world!
Hello World被正確輸出了。
包(Package)是.NET中共享代碼的一種方式。在.NET中,包的格式是nupkg, nupkg文件是一個ZIP壓縮文件, 裏面包含了.NET程序集和一個包含元數據的xml文件
。
在.NET中,最著名的包是JSON.NET, 又稱Newtonsoft.Json.它提供了一個JSON序列化和反序列化的API。咱們能夠從NuGet.org中下載最新版本11.0.2的nupkg文件,並解壓放置在咱們當前的代碼目錄的packages\Newtownsoft.Json\11.0.2子目錄下。
爲了演示如何手動導入包來編譯項目, 咱們修改Program.cs
, 輸出一個序列化以後的對象, 代碼以下:
class Program { static void Main(string[] args) => System.Console.WriteLine( Newtonsoft.Json.JsonConvert.SerializeObject(new { greeting = "Hello World!" })); }
而後咱們使用以下命令,編譯Program.cs
C:\test>dotnet "C:\Program Files\dotnet\sdk\2.1.400\Roslyn\bincore\csc.dll" -reference:"C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Runtime.dll" -reference:"C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Console.dll" -reference:"C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Collections.dll" -reference:.\packages\Newtonsoft.Json\11.0.2\lib\netstandard1.3\Newtonsoft.Json.dll -out:Program.dll Program.cs
這裏爲何要引入
System.Collections.dll
呢?
緣由是咱們在代碼中使用了匿名類型new { greeting = "Hello World!" }
, 對於匿名類型, C#編譯器會爲其生成一個.Equals
的方法, 這個方法調用了定義在System.Collections.dll
中 的System.Collections.Generic.EqualityComparer
方法
編譯成功,可是會出現一些警告(Warning)
Program.cs(4,35): warning CS1701: Assuming assembly reference 'System.Runtime, Version=4.0.20.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' used by 'Newtonsoft.Json' matches identity 'System.Runtime, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' of 'System.Runtime', you may need to supply runtime policy
這意味着.Newtonsoft.Json的做者建立Newtonsoft.Json.dll
時,是使用4.0.20.0的System.Runtime
程序集, 可是系統當前使用的System.Runtime
程序集是4.2.0.0版本的。編譯器警告你4.0.20.0和4.2.0.0版本能夠有很大的差別。不過幸運的是,這些差別都是向後兼容的(all backwards comptible), 因此Newtonsoft.Json.dll
能夠正常工做。若是想去除這個警告,咱們可使用-nowarn:CS1701
C:\test>dotnet "C:\Program Files\dotnet\sdk\2.1.400\Roslyn\bincore\csc.dll" -reference:"C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Runtime.dll" -reference:"C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Console.dll" -reference:"C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Collections.dll" -reference:.\packages\Newtonsoft.Json\11.0.2\lib\netstandard1.3\Newtonsoft.Json.dll -nowarn:CS1701 -out:Program.dll Program.cs
在上一步中,咱們編譯了一個引用了Newtonsoft.Json.dll
的.NET Core程序,在引用Newtonsoft.Json.dll
以前,代碼能夠正常運行,可是引用Newtownsoft.Json.dll
以後,程序運行失敗。
C:\test> dotnet Program.dll Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed'. The system cannot find the file specified.
.NET是一個動態連接的運行時。編譯器會爲Program.dll
程序集添加Newtonsoft.Json.dll
的引用,可是不會複製它的代碼。.NET Core運行時指望在Program.dll
運行的時候,動態查找並加載一個Newtonsoft.Json.dll
文件。這一點對於System.Runtime.dll
, System.Console.dll
以及其餘System.*
的程序集也是同樣。
.NET Core能夠配置查找Newtonsoft.Json.dll
文件的目錄範圍,這裏咱們先簡單的將Newtownsoft.Json.dll
拷貝到與Program.dll
相同的目錄中, 而後從新運行Program.dll
C:\test>copy .\packages\Newtonsoft.Json\11.0.2\lib\netstandard1.3\Newtonsoft.Json.dll Newtonsoft.Json.dll C:\test>dotnet Program.dll {"greeting":"Hello World!"}
咱們預期的結果出現了。
注意:這裏不須要拷貝System.Runtime.dll和System.Console.dll, 緣由是他們存在於Microsoft.NETCore.App共享框架中,咱們已經在runtimeconfig.json中配置過了。
正如上一節所說的.NET Core能夠配置查找動態連接程序集的位置。
這些位置包括:
deps.json
是一個記錄.NET Core中依賴清單的文件。它能夠用來配置動態連接的程序集。
deps.json
文件中定義了動態連接的依賴列表。一般這個文件在Visual Studio中是自動生成,並且在生產環境中也會很是的大。可是它確實是一個純文本文件,因此咱們可使用任何編輯器編寫它。
下面咱們手動添加一個Program.deps.json
, 代碼以下:
{ "runtimeTarget": { "name": ".NETCoreApp,Version=v2.1" }, "targets": { ".NETCoreApp,Version=v2.1": { "Newtonsoft.Json/11.0.2": { "runtime": { "lib/netstandard1.3/Newtonsoft.Json.dll": {} } } } }, "libraries": { "Newtonsoft.Json/11.0.2": { "type": "package", "serviceable": false, "sha512": "" } } }
如今咱們刪除以前拷貝過來的Newtonsoft.Json.dll
, 而後從新運行Program.dll
C:\test>del Newtonsoft.Json.dll C:\test>dotnet Program.dll Error: An assembly specified in the application dependencies manifest (Program.deps.json) was not found: package: 'Newtonsoft.Json', version: '11.0.2' path: 'lib/netstandard1.3/Newtonsoft.Json.dll'
因而可知,儘管咱們添加了deps.json
, .NET Core依然須要一些其餘的信息來探測deps.json
中定義的動態程序集。
這裏有3種方式來設置,你能夠選中一行任意一種方式設置搜索動態連接庫的目錄路徑,修改以後,{"greeting":"Hello World!"}
就會正常輸出出來。
這種一種方式是最佳的實現方式.咱們能夠添加一個Program.runtimeconfig.dev.json
,並在其中添加動態連接搜索目錄字段additionalProbingPaths
{ "runtimeOptions": { "additionalProbingPaths": [ "/Users/nmcmaster/code/packages/" ] } }
註解:這裏的配置相似於Transformed Config, 若是指定當前的環境是dev,它就會讀取Program.runtimeconfig.json, 並將Program.runtimeconfig.dev.json的內容覆蓋進去
咱們仍是可使用dotnet exec
命令並指定--additionalprobingpath
參數來配置檢索的目錄。
C:\test> dotnet exec --additionalprobingpath ./packages/ Program.dll
固然你也能夠直接在*.runtimeconfig.json
中添加additionalProbingPaths
字段
{ "runtimeOptions": { "framework": { "name": "Microsoft.NETCore.App", "version": "2.1.2" }, "additionalProbingPaths": [ "./packages/" ] } }