深入 .NET Core 基础 - 2:共享框架

深入 .NET Core 基础 - 2:共享框架

原文地址:https://natemcmaster.com/blog/2018/08/29/netcore-primitives-2/

共享框架从 .NET Core 1.0 就成为基础部分。ASP.NET Core 从 .NET Core 2.1 开始也作为共享框架发布。你可能没有注意到该进展是否顺利。但是,这里有一些关于该设计的颠簸和讨论。本文将深入到共享框架,并探讨它的一些常见陷阱。

1. 基础

.NET Core 应用程序有两种运行模型:基于框架或者自包含。在我的 MacBook 上,最小的自包含 ASP.NET Core 应用程序的尺寸是 83MB 和 350 个文件。另一方面,最小的框架依赖应用的尺寸是 239KB 和 5 个文件。

可以通过下面的命令来生成两种应用程序

dotnet new web
dotnet publish --runtime osx-x64 --output bin/self_contained_app/
dotnet publish --output bin/framework_dependent_app/

在应用程序运行的时候,两种模式的功能是等效的。所以为什么存在不同类型的模型?如微软的文档所述:

框架依赖的发布基于共享的系统范围的 .NET Core 版本......

而自包含的发布不依赖与目标系统上的共享组件。所有的组件......都包含在应用程序中。

该文档非常好地解释了每种模式的优点。

2. 共享框架

长短短说,.NET Core 共享框架是一个包含程序集 (*.dll 文件) 的,不在应用程序文件夹中的文件夹。这些程序集一起版本化和发布。该文件夹是 "共享的系统范围的 .NET Core 版本" 的一部分,通常在 C:/Program Files/dotnet/shared 文件夹中。

当你执行 dotnet.exe WebApp.dll 的时候,.NET Core 宿主 必须:

  1. 发现你的应用所依赖的名称和版本
  2. 在公共位置找到这些依赖内容

这些依赖可以在多个位置发现,包括,但是不限于,这些共享框架。在上一篇文章中,我已经总结了 deps.jsonruntimeconfig.json 文件是如何配置宿主的行为。请查看它来得到更详细的说明。

.NET Core 宿主读取 *.runtimeconfig.json 文件来得到需要加载哪个共享框架。其内容可能类似于如下:

{
  "runtimeOptions": {
    "framework": {
      "name": "Microsoft.AspNetCore.App",
      "version": "2.1.1"
    }
  }
}

共享框架名称 只是一个名称。根据约定,该名称以 App 结束,但可以是任何名称,比如 "FooBananaShark"。

共享框架版本 表示最小版本。.NET Core 宿主从不运行在最小版本上,而是试图运行在更高的版本上。

2.1 我已经安装的共享框架是哪些?

执行 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]

2.2 比较 Microsoft.NETCore.App,AspNetCore.App 和 AspNetCore.All

在 .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

2.3 与 NuGet package 的关系

.NET Core SDK 生成 runtimeconfig.json 文件。在 .NET Core 1 和 2 中,它使用项目文件中的两个片段来决定该文件中框架部分的内容:

  1. MicrosoftNETPlatformLibrary 属性。默认对于所有的 .NET Core 项目设置为 Microsoft.NETCore.App
  2. NuGet 恢复的结果,它必须包含一个同名的包

对于所有的项目,.NET Core SDK 对 Microsoft.NETCore.App添加隐式的包引用。ASP.NET Core 默认设置 MicrosoftNETPlatformLibraryMicrosoft.AspNetCore.App

此 NuGet 包,实际上,并不提供共享框架。重复一遍,这个 NuGet 包 不提供共享框架(后面我还会再次重复)。该 NuGet 包仅仅为编译器提供 API 集和很少的其它 SDK 部分。共享框架文件来自于安装的运行时,或者在 Visual Studio 中打包,Docker 映像,以及一些 Azure 服务。

2.4 版本前滚

如前所述,runtimeconfig.json 是最小版本号。实际使用的版本基于版本前滚策略。常见的方式:

  • 如果某个应用程序的最小版本是 2.1.0,那么 2.1.* 的最高版本将会被应用

我将会在下一篇详细说明。

2.5 层化的共享框架

此特性在 .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"
  }
}

  

2.6 多层查找

此特性在 .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

2.7 ReadyToRun

共享框架中的程序集语境使用名为 crossgen 的工具进行了预优化。该过程生成了 ReadyToRun 版本的程序集,其对特定版本的操作系统和 CPU 架构进行了优化。主要的性能收益在于缩短了 JIT 花费在启动阶段的代码准备时间。

3. 陷阱

我想每个 .NET Core 开发者都可能在某个时候落入某个陷阱中。我将试图说明它是如何发生的。

3.1 HTTP Error 502.5 Process Failure

当在 IIS 上寄宿 ASP.NET Core 或者在 Azure 的 Web Services 上寄宿的时候,这是最常见的问题。典型发生在开发者升级项目之后,或者部署到一台最近没有更新的机器上。实际的错误来自于共享框架没有被找到,导致 .NET Core 应用程序不能启动。当 .NET Core 不能运行应用程序,IIS 输出 502.5 错误,但是并没有暴露内部的错误信息。

3.2 “The specified framework was not found”

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 文件中指定了特定的框架名称和版本,而宿主不能使用多级查找和前向错略找到对应的版本。如前所述。

3.3 为 Microsoft.AspNetCore.App 更新 NuGet 包

NuGet 中的 Microsoft.AspNetCore.App 包不提供共享框架。仅仅提供用于 C#/ F# 编译器的 API 和一些 SDK 支持。你必须单独下载并安装共享框架。

另外,基于前滚策略,你也不必升级 NuGet 包的版本来使得你的应用程序运行在更新的共享框架版本上。

将共享框架表现为项目文件中的一个 NuGet 包可能是 ASP.NET Core 团队的一个设计错误。表现共享框架的包并不是一个正常的包。不像多数的其它包,它不是自满足的。我们有理由期待在项目使用 <PackageReference> 引用某个包的时候,NuGet 能够安装任何所需要的内容,令人沮丧的是,这里的包偏离了该模式。有多个提案建议修复该问题。我期望某个提案很快落地。

3.4 <PackageReference Include="Microsoft.AspNetCore.App" />

所有其它的 <PackageReference> 都必须包含 Version 属性。缺失版本的包引用仅仅工作于项目开始部分使用 <Project Sdk="Microsoft.NET.Sdk.Web">。且仅仅工作于 Microsoft.AspNetCore.{App, All} 包。Web SDK 将基于项目中的其它值自动提取这些包的版本,例如 <TargetFramework> 和 <RuntimeIdentifier>

如果你为包引用元素指定了版本的话,或者你没有使用 Web SDK,该魔法将不会工作。很难建议一个好的解决方案,因为最佳的方式依赖于你理解的水平和项目的类型。

3.5 发布修剪

当你使用 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 传递升级或者通过第三方依赖升级,就会无意中发生。

3.6 困惑于共享框架的目标框架名称

很容易认为:"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“

3.7 困惑的项目设置

最后一个提醒的陷阱是项目设置。许多术语和设置的名称并不确切。使用令人困惑的术语,所以,如果你搞混了它们,这并不是你的过错。

下面,我列出常见的项目设置,以及实际的含义。

<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>

4. 总结

共享框架是 .NET Core 一个可选特性,我可以合理地认为多数的用户憎恨陷阱。我仍然认为对于 .NET Core 开发者来说,理解背后发生了什么是有用的。并期望这是关于共享框架的有用的总结。我还给出了官方的链接和指南,以便帮助你找到更多信息。如果有任何问题,欢迎留言。

More

https://github.com/dotnet/cli/blob/v2.1.400/Documentation/specs/runtime-configuration-file.md

原文地址:https://www.cnblogs.com/haogj/p/12008207.html

时间: 2024-10-11 14:34:40

深入 .NET Core 基础 - 2:共享框架的相关文章

深入理解.NET Core的基元(二) - 共享框架

原文:Deep-dive into .NET Core primitives, part 2: the shared framework 作者:Nate McMaster 译文:深入理解.NET Core的基元(二) - 共享框架 作者: Lamond Lu 本篇是之前翻译过的<深入理解.NET Core的基元: deps.json, runtimeconfig.json, dll文件>的后续,这个系列作者暂时只写了3篇,虽然有一些内容和.NET Core 3.0已经不兼容了,但是大部分的原理

ASP.NET Core 配置 EF 框架服务 - ASP.NET Core 基础教程 - 简单教程,简单编程

原文:ASP.NET Core 配置 EF 框架服务 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 配置 EF 框架服务 上一章节中我们了解了 Entity Framework 的基本工作原理和 DbContext ,我们也创建了一个自己的 HelloWorldDBContext. 本章节我们就来讲讲如何设置我们的 EF 框架来链接到 SQLite 数据库 配置 EF 框架服务 要让我们的 EF 框架的 DBContext 能够运行起来,我们需要更改一

ASP.NET Core 使用 EF 框架查询数据 - ASP.NET Core 基础教程 - 简单教程,简单编程

原文:ASP.NET Core 使用 EF 框架查询数据 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 使用 EF 框架查询数据 上一章节我们学习了如何设置和初始化数据库,以及如何创建迁移代码和应用迁移代码.本章节我们就学习如何使用 EF 框架来查询数据库,至于添加和修改,后面的章节中我们会慢慢学习到 添加测试数据 我们首先使用 SQLite Studio 添加三条数据 ID Name 1 李白 2 杜甫 3 白居易 使用 SQLite Studio

深入 .NET Core 基础 - 1:deps.json, runtimeconfig.json 以及 dll

深入 .NET Core 基础:deps.json, runtimeconfig.json 以及 dll 原文地址:https://natemcmaster.com/blog/2017/12/21/netcore-primitives/ 1. .NET Core 应用程序基础 我学习过使用 gcc,C++ 和 vim 编程.当我开始使用 C# 和 .NET 的时候,点击 Visual Studio 中的 运行 按钮就是魔法,也带者失望.失望 - 不是因为我希望编写 Makefile - 而是因为

UIKit,Core Data , Core Graphics, Core Animation,和OpenGLES框架

iOS的主要框架介绍 框架是一个目录,这个目录包含了共享库,访问共享库里代码的头文件,和其它的图片和声音的资源文件.一个共享库定义的方法或函数可以被应用程序调用. IOS提供了很多你可以在应用程序里调用的框架.要使用一个框架,需要将它添加到你的项目中,你的项目才可以使用它.许多应用程序都使用了如Foundation.UIKit.和Core Graphics这些框架.根据你为应用程序选择的模版,相关的框架就已经被自动引入了.如果默认加入的框架不能满足你的应用程序的需求,你也可以加入需要的框架. 看

【SF】开源的.NET CORE 基础管理系统 - 安装篇

[SF]开源的.NET CORE 基础管理系统 -系列导航 1.开发必备工具 IDE:VS2017 运行环境:netcoreapp1.1 数据库:SQL Server 2012+ 2.获取最新源代码 http://git.oschina.net/sframework/SF-Boilerplate 直接使用VS Git工具进行克隆到本地存储库 3.打开并编译项目 打开并编译还原Nuget包,如果还原失败,请关闭VS重新打开项目重新编译. 如访问国外NuGet服务器速度不稳定的情况下,可以使用博客园

ASP.NET Core 动作结果 - ASP.NET Core 基础教程 - 简单教程,简单编程

原文:ASP.NET Core 动作结果 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 动作结果 前面的章节中,我们一直使用简单的 C# 类作为控制器. 虽然这些类不是从基类派生的,但仍然可以在 MVC 中使用这种方法. 当然了,对于控制器,但更常见的做法是从 Microsoft.AspNetCore.Mvc 命名空间中提供的控制器基类中派生控制器.本章中,我们将尝试这么做,并且学习动作结果 ( Action Results ). 动作结果 ( Act

ASP.NET Core 设置和初始化数据库 - ASP.NET Core 基础教程 - 简单教程,简单编程

原文:ASP.NET Core 设置和初始化数据库 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 设置和初始化数据库 上一章节中我们已经设置和配置好了 EF 框架服务,本章节我们就来学习如何使用 EF 框架设置和初始化数据库 初始化数据库 初始化数据库的方法之一是使用 EF 框架来创建数据库,仅仅需要两步就能完成 第一步,给我们的 HelloWorld 项目添加迁移 ( migration ) 代码 迁移代码是 C# 代码,用来在数据库系统中创建数据库

ASP.NET Core 视图 - ASP.NET Core 基础教程 - 简单教程,简单编程

原文:ASP.NET Core 视图 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 视图 花了几章节,终于把 ASP.NET Core MVC 中的 C 控制器涉及的七七八八了,本章节我们来学习下 V,也就是视图部分. ASP.NET Core MVC 应用程序中,没有任何内容像页面,并且在 URL 中指定路径时, 它也不包含与页面直接对应的任何内容. ASP.NET Core MVC 应用程序中最接近页面的东西被称为视图 是不是很拗口,哈哈,页面就是