实验环境:
- win10 x64
- vs2019 16.3
- .netcore 2.2
实验目的:
- 探究.net core程序的开发的编译、打包、发布过程
- 探究vs工程结构
一、新建.net core控制台程序
新建项目:
新建成功后vs组织结构:
此时磁盘的组织结构为:
查看主要的三个文件内容:consoledemo.sln、consoledemo.csproj、Program.cs
consoledemo.sln:
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29215.179
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "consoledemo", "consoledemo.csproj", "{5AD28B6C-4558-4282-A4E7-203B0A23F17F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5AD28B6C-4558-4282-A4E7-203B0A23F17F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5AD28B6C-4558-4282-A4E7-203B0A23F17F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5AD28B6C-4558-4282-A4E7-203B0A23F17F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5AD28B6C-4558-4282-A4E7-203B0A23F17F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {84FCC3C5-89AF-42C4-90E2-99AA33719410}
EndGlobalSection
EndGlobal
consoledemo.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>
</Project>
Program.cs:
using System;
namespace consoledemo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
- 从上面的图中可以看出,这个控制台运行在netcoreapp2.2下面,那么它指定的运行时环境在哪个位置呢:
为什么选择这个版本,而不是其他的,是因为工程文件中指定的版本是2.2,那么就从2.2向上找最接近的- 从vs中只观察到依赖sdk,那么依赖的sdk中具体的库文件在哪呢:
二、编译工程
2.1 工程右键->重新生成
生成后的目录:
解释一下上面文件的意义:
- consoledemo.dll
这个就是我们写的代码编译后的结果,最主要的!
- consoledemo.runtimeconfig.json
如果我们编译生成的dll文件是依赖运行主机上的dotnetcore环境的话,那么我们需要这个文件来执行运行的dotnet版本和其他一些信息(如:依赖的外部dll的搜索路径)。一般我们需要这个文件来执行运行的dotnetcore版本!
- consoledemo.deps.json
这个文件主要是用来描述外部依赖的,当我们引用了其他包的时候,比如“Newtonsoft.Json”那么我们需要在这个文件中标明一下(注意:这里只是标明程序集名称并没有标明搜索路径),当然如果我们把“Newtonsoft.Json”直接放在了这个目录里就不需要这个文件了;一般我们在调试的时候,ide不会把“Newtonsoft.Json”拷贝到这个目录的,所以调试的时候需要,而我们发布的时候ide是把“Newtonsoft.Json”拷贝到这个目录的,所以发布的时候一般不需要。
- consoledemo.runtimeconfig.dev.json
这个文件和“consoledemo.deps.json”配合使用在ide的调试环境。上面在介绍“consoledemo.deps.json”文件的时候有解释到它没有标明程序集的搜索路径,那么在ide调试的时候是怎么找到“Newtonsoft.Json”的呢?这就是“consoledemo.runtimeconfig.dev.json”的作用了,在这个文件里可以配置程序集的搜索路径,不过只能配置绝对地址不能配置相对地址,也就是说它仅适合调试环境。
- consoledemo.pdb
这个文件是调试用的,不做解释。
2.2 运行dll文件
直接在命令行中运行:dotnet .\consoledemo.dll
那么,将上面的文件数量精简到最小化再运行:
此时打开consoledemo.runtimeconfig.json,观察里面内容:
{
"runtimeOptions": {
"tfm": "netcoreapp2.2",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "2.2.0"
}
}
}
可以看出“consoledemo.runtimeconfig.json”中就只是描述了dotnetcore运行时的版本。
将"consoledemo.dll"和"consoledemo.runtimeconfig.json"这两个文件上传到centos上运行:
效果也是一样的。
2.3 修改工程引入外部依赖:Newtonsoft.Json
nuget中引入依赖:
此时查看工程文件:consoledemo.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
</ItemGroup>
</Project>
可以看到,在工程文件中引入了nuget依赖
接着修改Program.cs代码:
using System;
namespace consoledemo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(new { name = "小明", age = 20 }));
}
}
}
2.4 重新编译工程
工程右键->重新生成,生成后的目录如下所示:
可以看到,还是这5个文件,此时你是不是奇怪Newtonsoft.Json怎么没有输出到这个目录?
答案是:这是“调试”模式不是“发布”模式。
此时打开consoledemo.deps.json,查看如下:
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v2.2",
"signature": "4bf20b3f4b187ddd01af96ea47dda577c5263775"
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v2.2": {
"consoledemo/1.0.0": {
"dependencies": {
"Newtonsoft.Json": "12.0.2"
},
"runtime": {
"consoledemo.dll": {}
}
},
"Newtonsoft.Json/12.0.2": {
"runtime": {
"lib/netstandard2.0/Newtonsoft.Json.dll": {
"assemblyVersion": "12.0.0.0",
"fileVersion": "12.0.2.23222"
}
}
}
}
},
"libraries": {
"consoledemo/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Newtonsoft.Json/12.0.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-rTK0s2EKlfHsQsH6Yx2smvcTCeyoDNgCW7FEYyV01drPlh2T243PR2DiDXqtC5N4GDm4Ma/lkxfW5a/4793vbA==",
"path": "newtonsoft.json/12.0.2",
"hashPath": "newtonsoft.json.12.0.2.nupkg.sha512"
}
}
}
可以看到,里面描述了依赖的Newtonsoft.Json信息,但是没有说明在哪个位置可以找到它。
再打开consoledemo.runtimeconfig.dev.json,查看内容如下:
{
"runtimeOptions": {
"additionalProbingPaths": [
"C:\\Users\\AUAS\\.dotnet\\store\\|arch|\\|tfm|",
"C:\\Users\\AUAS\\.nuget\\packages",
"C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder"
]
}
}
可以看到,这个文件里面清晰的描述了程序集的搜索路径,有了这个文件也就能根据“consoledemo.deps.json”声明的依赖信息去寻找具体的dll文件了。
2.5 运行生成的dll
首先删掉“consoledemo.pdb”文件,这个文件不用管。
打开命令行直接运行如下:
可以看到,能正常得到结果。
此时删除掉consoledemo.runtimeconfig.dev.json会怎么样呢?
可以看到,报错了,虽然在consoledemo.deps.json中声明了“Newtonsoft.Json”信息,但是由于缺少程序集的搜索路径仍然找不到“Newtonsoft.Json.dll”
如果把consoledemo.deps.json也删除掉怎么样呢?
可以看到,更是不行了!
三、打包工程
工程右键->打包,观察结果
可以看到在Debug目录下生成了一个consoledemo.1.0.0.nupkg文件,现在我们就可以方便的将这个nuget包发布出去共享了。
“consoledemo.1.0.0.nupkg”这个文件其实也是一个压缩包,查看解压后的目录:
这里解释一下里面的内容:
- “[Content_Types].xml”文件、“_rels”目录、“package”目录都是和包的格式定义有关的,我们不用关心这些。
- consoledemo.nuspec:这个文件描述了当前nuget包的元数据信息,包括:id、版本号、作者、依赖包,详细如下:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>consoledemo</id>
<version>1.0.0</version>
<authors>consoledemo</authors>
<owners>consoledemo</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Package Description</description>
<dependencies>
<group targetFramework=".NETCoreApp2.2">
<dependency id="Newtonsoft.Json" version="12.0.2" exclude="Build,Analyzers" />
</group>
</dependencies>
</metadata>
</package>
- lib:里面是这个nuget包提供的程序集文件,按照运行环境组织目录,如下:
四、发布工程
工程右键->发布:
点击“创建发布文件”后:
点击摘要下面的配置:
解释一下:
- 目标框架:选择程序的运行时即可
- 部署模式:“框架依赖”和“独立”二选一,它们又称为“framework-dependent”和“self-contained”
框架依赖就是自己不携带和操作系统相关的东西,依赖于.netcore环境(也可以是具体操作系统上的.netcore,也可以是任意操作系统上的.netcore,后者称之为“可移植”)
独立就是发布的时候将.netcore运行环境也打包了进来,好处是可以单独运行,坏处是生成的文件特别大,比如一个简单的”Hello World” 独立部署就要100多M,而框架依赖方式只有几十kb - 目标运行时:可以选择一个具体的操作系统版本,也可以选择“可移植”也就是任意操作系统版本,不过在部署模式选择“独立”的时候,这里就不能选择可移植了。
这里我们选择“框架依赖”、“可移植”(默认就是这两个),最后点击发布:
可以看到,发布后是把外部的依赖“Newtonsoft.Json.dll”输出了的,直接运行如下:
可以看到,运行正常!
那么发布后的这几个文件能精简到什么程度呢?
可以看到,只能精简到这了,再删除就会报错了,你可以试试。
此时将精简后的这三个文件放到linux上运行也是可以的,这里不再实验。
这里可能会有疑问,为什么“consoledemo.deps.json”可以删除?
因为它是记录的依赖信息,当具体的程序集都在当前目录的时候,它就不是那么重要了,还有你是否还记得nuget包里lib下存储的东西呢?