深入 .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 宿主 必须:
发现你的应用所依赖的名称和版本
在公共位置找到这些依赖内容
这些依赖可以在多个位置发现,包括,但是不限于,这些共享框架。在上一篇文章中,我已经总结了 deps.json
和 runtimeconfig.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 中,它使用项目文件中的两个片段来决定该文件中框架部分的内容:
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 服务。
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
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
深入 .NET Core 基础 - 2:共享框架的更多相关文章
- 深入理解.NET Core的基元(二) - 共享框架
原文:Deep-dive into .NET Core primitives, part 2: the shared framework 作者:Nate McMaster 译文:深入理解.NET Co ...
- .net core +codefirst(.net core 基础入门,适合这方面的小白阅读) 【我们一起写框架】领域驱动设计的CodeFirst框架(一)—序篇
.net core +codefirst(.net core 基础入门,适合这方面的小白阅读) 前言 .net core mvc和 .net mvc开发很相似,比如 视图-模型-控制器结构.所以. ...
- ASP.NET Core Identity 框架 - ASP.NET Core 基础教程 - 简单教程,简单编程
原文:ASP.NET Core Identity 框架 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core Identity 框架 前面我们使用了 N 多个章节, ...
- ASP.NET Core 使用 EF 框架查询数据 - ASP.NET Core 基础教程 - 简单教程,简单编程
原文:ASP.NET Core 使用 EF 框架查询数据 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 使用 EF 框架查询数据 上一章节我们学习了如何设置 ...
- ASP.NET Core 配置 EF 框架服务 - ASP.NET Core 基础教程 - 简单教程,简单编程
原文:ASP.NET Core 配置 EF 框架服务 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 配置 EF 框架服务 上一章节中我们了解了 Entity ...
- 深入 .NET Core 基础 - 1:deps.json, runtimeconfig.json 以及 dll
深入 .NET Core 基础:deps.json, runtimeconfig.json 以及 dll 原文地址:https://natemcmaster.com/blog/2017/12/21/n ...
- UIKit,Core Data , Core Graphics, Core Animation,和OpenGLES框架
iOS的主要框架介绍 框架是一个目录,这个目录包含了共享库,访问共享库里代码的头文件,和其它的图片和声音的资源文件.一个共享库定义的方法或函数可以被应用程序调用. IOS提供了很多你可以在应用程序 ...
- 【SF】开源的.NET CORE 基础管理系统 - 安装篇
[SF]开源的.NET CORE 基础管理系统 -系列导航 1.开发必备工具 IDE:VS2017 运行环境:netcoreapp1.1 数据库:SQL Server 2012+ 2.获取最新源代码 ...
- 【SF】开源的.NET CORE 基础管理系统 -介绍篇
[SF]开源的.NET CORE 基础管理系统 -系列导航 1.环境: .NET Core SDK (https://www.microsoft.com/net/core) SQL Server or ...
随机推荐
- [python]一些常用的python知识总结
Pthon知识积累,遇到的各种python问题汇总 json.dumps()和json.loads()是json格式处理函数 json.dumps()函数是将一个Python数据类型列表进行json格 ...
- Java抽象类、接口、内部类
抽象类的概念: 1.Java中可以定义没有方法体的方法,还方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类: 2.如,shape类计算周长和面积的方法无法确定,那么就可以将这样 ...
- CSPS模拟 80
题还没改完就来臭不要脸的写反思了. (主要因为太困了懒得改了) (还因为T2看起来太过弱智) (也许等我生物钟恢复正常后能当做课余消遣水一水) statistic:skyh接了两杯水,真能喝啊 然后他 ...
- 推荐一款Diffy:Twitter的开源自动化测试工具
1. Diffy是什么 Diffy是一个开源的自动化测试工具,是一种Diff测试技术.它能够自动检测基于Apache Thrift或者基于HTTP的服务.通过同时运行新/老代码,对比运行结果,发现潜在 ...
- day5-基本数据类型总结
一.数字int(..)二.字符串replace/find/join/strip/startswith/split/upper/lower/format tempalte = "i am {n ...
- pip的简单用法
pip的用法: 其实跟linux的yum很像,它可以帮我们安装python所需要的环境包,并且可以包解决依赖关系 eg: 列出已安装的包 pip list 安装要安装的包 pip install xx ...
- 【自然语言处理】利用LDA对希拉里邮件进行主题分析
首先是读取数据集,并将csv中ExtractedBodyText为空的给去除掉 import pandas as pd import re import os dir_path=os.path.dir ...
- C# 获取系统当前登录用户(管理员身份运行同样有效)
今天学习下怎么用.Net获取系统当前登陆用户名,因为目前网上基本只有最简单的方式,但以管理员身份运行的话就会获取不到,所以特整理一下作为分享,最后附带参考文档,方便深究的童鞋继续学习. ======= ...
- PHP Laravel5实现的RBAC权限管理操作示例
根据不同的权限,在菜单栏显示不同的功能,只对菜单进行了限制,若对路由也进行限制,可以根据菜单的例子,请自行完善,开发.下面请认真学习一下laravel的RBAC设计 1.建表(用户表.角色表.权限表. ...
- 百度全景地图使用时提示flash版本过低 如何处理?
从Chrome 69.0 版本起,Flash权限受到进一步限制,默认仅在当前浏览器会话有效.关闭Enable Ephemeral Flash Permissions ,才能看到 “Add”按钮.解决方 ...