原文地址:https://natemcmaster.com/blog/2018/08/29/netcore-primitives-2/javascript
共享框架從 .NET Core 1.0 就成爲基礎部分。ASP.NET Core 從 .NET Core 2.1 開始也做爲共享框架發佈。你可能沒有注意到該進展是否順利。可是,這裏有一些關於該設計的顛簸和討論。本文將深刻到共享框架,並探討它的一些常見陷阱。java
.NET Core 應用程序有兩種運行模型:基於框架或者自包含。在個人 MacBook 上,最小的自包含 ASP.NET Core 應用程序的尺寸是 83MB 和 350 個文件。另外一方面,最小的框架依賴應用的尺寸是 239KB 和 5 個文件。git
能夠經過下面的命令來生成兩種應用程序github
dotnet new web dotnet publish --runtime osx-x64 --output bin/self_contained_app/ dotnet publish --output bin/framework_dependent_app/
在應用程序運行的時候,兩種模式的功能是等效的。因此爲何存在不一樣類型的模型?如微軟的文檔所述:web
框架依賴的發佈基於共享的系統範圍的 .NET Core 版本......json
而自包含的發佈不依賴與目標系統上的共享組件。全部的組件......都包含在應用程序中。windows
該文檔很是好地解釋了每種模式的優勢。bash
長短短說,.NET Core 共享框架是一個包含程序集 (*.dll 文件) 的,不在應用程序文件夾中的文件夾。這些程序集一塊兒版本化和發佈。該文件夾是 "共享的系統範圍的 .NET Core 版本" 的一部分,一般在 C:/Program Files/dotnet/shared
文件夾中。網絡
當你執行 dotnet.exe WebApp.dll
的時候,.NET Core 宿主 必須:架構
發現你的應用所依賴的名稱和版本
在公共位置找到這些依賴內容
這些依賴能夠在多個位置發現,包括,可是不限於,這些共享框架。在上一篇文章中,我已經總結了 deps.json
和 runtimeconfig.json
文件是如何配置宿主的行爲。請查看它來獲得更詳細的說明。
.NET Core 宿主讀取 *.runtimeconfig.json
文件來獲得須要加載哪一個共享框架。其內容可能相似於以下:
{ "runtimeOptions": { "framework": { "name": "Microsoft.AspNetCore.App", "version": "2.1.1" } } }
共享框架名稱 只是一個名稱。根據約定,該名稱以 App
結束,但能夠是任何名稱,好比 "FooBananaShark"。
共享框架版本
執行 dotnet --list-runtimes
。它將會顯示名稱、版本和共享框架的位置。對於 .NET Core 3.1,共享框架的列表以下所示。
dotnet --list-runtimes Microsoft.AspNetCore.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
在 .NET Core 2.2 中,有以下三種共享框架:
框架名稱 | 說明 |
---|---|
Microsoft.NETCore.App | 基礎運行時. 它支持相似 System.Object , List , string , 內存管理,文件和網絡 I/O,線程等等 |
Microsoft.AspNetCore.App | 默認的 Web 運行時. 它導入了 Microsoft.NETCore.App, 並添加了使用 Kestrel Mvc、SingalR、Razor 和部分 EF Core 構建 HTTP 服務的 API |
Microsoft.AspNetCore.All | 集成了第三方內容。它導入了 Microsoft.AspNetCore.App. 加入了對 EF Core + SQLite 支持, 使用 Redis 的擴展, 從 Azure Key Vault 進行配置, 以及更多內容. (在 .NET Core 3.0 中將被退役 deprecated in 3.0.) |
.NET Core 3.0 增長了 Microsoft.WindowsDesktop.App,並刪除了
Microsoft.AspNetCore.All
。
.NET Core SDK 生成 runtimeconfig.json
文件。在 .NET Core 1 和 2 中,它使用項目文件中的兩個片斷來決定該文件中框架部分的內容:
MicrosoftNETPlatformLibrary
屬性。默認對於全部的 .NET Core 項目設置爲 Microsoft.NETCore.App
NuGet 恢復的結果,它必須包含一個同名的包
對於全部的項目,.NET Core SDK 對 Microsoft.NETCore.App
添加隱式的包引用。ASP.NET Core 默認設置 MicrosoftNETPlatformLibrary
爲 Microsoft.AspNetCore.App
此 NuGet 包,實際上,並不提供共享框架。重複一遍,這個 NuGet 包 不提供共享框架(後面我還會再次重複)。該 NuGet 包僅僅爲編譯器提供 API 集和不多的其它 SDK 部分。共享框架文件來自於安裝的運行時,或者在 Visual Studio 中打包,Docker 映像,以及一些 Azure 服務。
如前所述,runtimeconfig.json 是最小版本號。實際使用的版本基於版本前滾策略。常見的方式:
若是某個應用程序的最小版本是 2.1.0,那麼 2.1.* 的最高版本將會被應用
我將會在下一篇詳細說明。
此特性在 .NET Core 2.1 被加入。
共享框架能夠依賴於其它的共享框架。它被引入來支持 ASP.NET Core,它被從包的運行時存儲轉換成了共享框架。
例如,若是你進入並查看 $DOTNET_ROOT/shared/Microsoft.AspNetCore.All/$version/
文件夾,你將會看到一個 Microsoft.AspNetCore.All.runtimeconfig.json
文件。
{ "runtimeOptions": { "tfm": "netcoreapp2.1", "framework": { "name": "Microsoft.AspNetCore.App", "version": "2.1.2" } } }
在 .NET Core 3.1 中,內容以下:
{ "runtimeOptions": { "tfm": "netcoreapp3.1", "framework": { "name": "Microsoft.NETCore.App", "version": "3.1.0" }, "rollForward": "LatestPatch" } }
此特性在 .NET Core 2.0 加入。
宿主會探測多個位置來尋找合適的共享框架。查找從 dotnet root 開始,這是包含 dotnet
可執行程序的文件夾。它能夠被環境變量 DOTNET_ROOT
所指定的文件夾覆蓋。第一個探測的位置是
$DOTNET_ROOT/shared/$name/$version
若是沒有合適的文件夾存在,將會使用 多層查找 試圖查看預約義的全局位置。此特性能夠經過環境變量 DOTNET_MULTILEVEL_LOOKUP=0
來關閉。默認的全局位置是:
OS | Location |
---|---|
Windows | C:\Program Files\dotnet (64-bit processes) C:\Program Files (x86)\dotnet (32-bit processes) (See in the source code) |
macOS | /usr/local/share/dotnet (source code) |
Unix | /usr/share/dotnet (source code) |
宿主將檢測的目錄位於:
$GLOBAL_DOTNET_ROOT/shared/$name/$version
共享框架中的程序集語境使用名爲 crossgen
的工具進行了預優化。該過程生成了 ReadyToRun
版本的程序集,其對特定版本的操做系統和 CPU 架構進行了優化。主要的性能收益在於縮短了 JIT 花費在啓動階段的代碼準備時間。
我想每一個 .NET Core 開發者均可能在某個時候落入某個陷阱中。我將試圖說明它是如何發生的。
當在 IIS 上寄宿 ASP.NET Core 或者在 Azure 的 Web Services 上寄宿的時候,這是最多見的問題。典型發生在開發者升級項目以後,或者部署到一臺最近沒有更新的機器上。實際的錯誤來自於共享框架沒有被找到,致使 .NET Core 應用程序不能啓動。當 .NET Core 不能運行應用程序,IIS 輸出 502.5
錯誤,可是並無暴露內部的錯誤信息。
It was not possible to find any compatible framework version The specified framework 'Microsoft.AspNetCore.App', version '2.1.3' was not found. - Check application dependencies and target a framework version installed at: /usr/local/share/dotnet/ - Installing .NET Core prerequisites might help resolve this problem: http://go.microsoft.com/fwlink/?LinkID=798306&clcid=0x409 - The .NET Core framework and SDK can be installed from: https://aka.ms/dotnet-download - The following versions are installed: 2.1.1 at [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] 2.1.2 at [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
此錯誤一般潛伏在 HTTP 502.5 以後,或者 Visual Studio 測試管理器的錯誤中。
它發生在 runtimeconfig.json 文件中指定了特定的框架名稱和版本,而宿主不能使用多級查找和前向錯略找到對應的版本。如前所述。
NuGet 中的 Microsoft.AspNetCore.App
包不提供共享框架。僅僅提供用於 C#/ F# 編譯器的 API 和一些 SDK 支持。你必須單獨下載並安裝共享框架。
另外,基於前滾策略,你也沒必要升級 NuGet 包的版原本使得你的應用程序運行在更新的共享框架版本上。
將共享框架表現爲項目文件中的一個 NuGet 包多是 ASP.NET Core 團隊的一個設計錯誤。表現共享框架的包並非一個正常的包。不像多數的其它包,它不是自知足的。咱們有理由期待在項目使用 <PackageReference>
引用某個包的時候,NuGet 可以安裝任何所須要的內容,使人沮喪的是,這裏的包偏離了該模式。有多個提案建議修復該問題。我指望某個提案很快落地。
全部其它的 <PackageReference> 都必須包含 Version
屬性。缺失版本的包引用僅僅工做於項目開始部分使用 <Project Sdk="Microsoft.NET.Sdk.Web">。且僅僅工做於 Microsoft.AspNetCore.{App, All}
包。Web SDK 將基於項目中的其它值自動提取這些包的版本,例如 <TargetFramework> 和 <RuntimeIdentifier>
若是你爲包引用元素指定了版本的話,或者你沒有使用 Web SDK,該魔法將不會工做。很難建議一個好的解決方案,由於最佳的方式依賴於你理解的水平和項目的類型。
當你使用 dotnet publish
建立一個框架依賴的應用程序時,SDK 使用 NuGet 恢復結果來決定哪一個程序集將會包含到發佈文件夾中。有些從 NuGet 包中複製過來,有些不會,由於它們被指望存在於共享框架中。
這很容易致使錯誤,由於 ASP.NET Core 做爲共享框架存在,也做爲 NuGet 包存在。修剪使用進行某些圖匹配來決定傳遞依賴、升級等等,來選取正確的文件。
例如,對於下面的項目
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.1" /> <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.1.9" />
MVC 其實是 Microsoft.AspNetCore.App 的一部分,可是,當執行 dotnet publish
的時候,它發現你的項目決定 升級 Microsoft.AspNetCore.Mvc.dll
到比 Microsoft.AspNetCore.App
的版本 2.1.1 更高的版本,因此,它將會把 Mvc.dll 放到發佈目錄中。
這樣是不理想的,由於你的應用程序尺寸變得更大了,而且你沒有獲得 ReadyToRun
優化以後的 Microsoft.AspNetCore.Mvc.dll
。若是你經過 ProjectReference 傳遞升級或者經過第三方依賴升級,就會無心中發生。
很容易認爲:"netcoreapp2.0" == "Microsoft.NETCore.App, v2.0.0"
。但這不是真的。目標框架名稱 (也稱爲 TFM) 在項目文件中使用 <TargetFramework> 指定。"netcoreapp2.0" 是一個友好的,你所使用的 .NET Core 版本名稱。
這個 TFM 的缺陷在於它過短了。它不能說明像多個共享框架這樣的問題,特定版本的補丁,版本前滾,輸出類型,以及自包含和框架依賴的發佈等等。SDK 將試圖從 TFM 來推斷這些設置,但它不能推斷全部的事情。
因此,精確地說,「netcoreapp2.0」 表示至少 V2.0.0 的 "Microsoft.NETCore.App「
最後一個提醒的陷阱是項目設置。許多術語和設置的名稱並不確切。使用使人困惑的術語,因此,若是你搞混了它們,這並非你的過錯。
下面,我列出常見的項目設置,以及實際的含義。
<PropertyGroup> <TargetFramework>netcoreapp2.1</TargetFramework> <!-- Actual meaning: * The API set version to use when resolving compilation references from NuGet packages. --> <TargetFrameworks>netcoreapp2.1;net471</TargetFrameworks> <!-- Actual meaning: * Compile for two different API version sets. This does not represent multi-layered shared frameworks. --> <MicrosoftNETPlatformLibrary>Microsoft.AspNetCore.App</MicrosoftNETPlatformLibrary> <!-- Actual meaning: * The name of the top-most shared framework --> <RuntimeFrameworkVersion>2.1.2</RuntimeFrameworkVersion> <!-- Actual meaning: * version of the implicit package reference to Microsoft.NETCore.App which then becomes the _minimum_ shared framework version. --> <RuntimeIdentifier>win-x64</RuntimeIdentifier> <!-- Actual meaning: * Operating system kind + CPU architecture --> <RuntimeIdentifiers>win-x64;win-x86</RuntimeIdentifiers> <!-- Actual meaning: * A list of operating systems and CPU architectures which this project _might_ run on. You still have to select one by setting RuntimeIdentifier. --> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.2" /> <!-- Actual meaning: * Use the Microsoft.AspNetCore.App shared framework. * Minimum version = 2.1.2 --> <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.1.2" /> <!-- Actual meaning: * Use the Microsoft.AspNetCore.Mvc package. * Exact version = 2.1.2 --> <FrameworkReference Include="Microsoft.AspNetCore.App" /> <!-- Actual meaning: * Use the Microsoft.AspNetCore.App shared framework. (This is new and unreleased...see https://github.com/dotnet/sdk/pull/2486) --> </ItemGroup>
共享框架是 .NET Core 一個可選特性,我能夠合理地認爲多數的用戶憎恨陷阱。我仍然認爲對於 .NET Core 開發者來講,理解背後發生了什麼是有用的。並指望這是關於共享框架的有用的總結。我還給出了官方的連接和指南,以便幫助你找到更多信息。若是有任何問題,歡迎留言。
Packages, metapackages and frameworks: https://docs.microsoft.com/en-us/dotnet/core/packages
.NET Core application deployment: https://docs.microsoft.com/en-us/dotnet/core/deploying/. Especially read the part about 「Framework-dependent deployments (FDD)」
Specs on runtimeconfig.json and deps.json:
https://github.com/dotnet/cli/blob/v2.1.400/Documentation/specs/runtime-configuration-file.md
The implementation of the shared framework lookup: https://github.com/dotnet/core-setup/blob/v2.1.3/src/corehost/cli/fxr/fx_muxer.cpp#L464