查看: 127|回复: 1

不安装运行时运行 .NET 程序

[复制链接]

6

主题

12

帖子

25

积分

新手上路

Rank: 1

积分
25
发表于 2022-12-18 19:07:16 | 显示全部楼层 |阅读模式
环境与工具
现阶段 .NET7 还在RC,所以我们选择安装 SDK 7.0.100-rc.1.22431.12 ,操作系统是 WIN10 64位,开发工具是 VS2022 17.4.0 Preview 2.1 。
正式版的 VS2022 是没办法选择目标框架 .NET7 的,但是其实可以手动改 csproj 文件,所以 VS2022 Preview 不是必须的。
临时加更干货分享

大家能看到这里,已是对我们的支持了。分享一组9月录制的C#零基础教程。我们喜欢做这样的分享,它足够的基础,对新手友好。如果需要的话,就来免费领取吧!



快来领取吧

资料免费自取:

由于内容过多不便呈现,需要视频教程和配套源码的小伙伴,可点击这里,添加我知乎主页个人说明处号码 免费分享
也可直接点击下方卡片:点击后自动复制威芯号,并跳转到威芯。搜索威芯号添加,内容已做打包,备注知乎
即可免费领取,注意查收!
兴致上来了做了张图,也是干货清单,需要的小伙伴直接来领就是了。

包含VS2022 / C#基础 .NET6/WPF/Winform零基础到各类实战!


继续正文
Console App
我们新建一个控制台程序,目标框架选择 NET7 (如果使用正式版的 VS2022 没有办法选择 net7 ,可以直接编辑 csproj 文件),右键项目选择“编辑项目文件”,在 PropertyGroup 节点下添加 PublishAot :
<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net7.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
  <!--aot发布-->
        <PublishAot>true</PublishAot>
  
    </PropertyGroup>

</Project>
修改 main 方法:
Console.WriteLine("Hello, AOT!");

Console.Read();
使用 dotnet 命令进行发布:
dotnet publish -r win-x64 -c Release


AOT 发布相比正常发布会慢一点,等待发布成功后,我们可以到以下目录查看 bin\Release\net7.0\win-x64\publish :


我们可以看到生成的 exe 文件只有 3.48MB ,相比普通单文件发布加裁剪过后的程序小了不少。


我们把这个 exe 程序复制到一台没有安装 .net 环境的服务器上,顺利运行起来了。
http://ASP.NET CORE
上面我们测试了一下控制台程序的 AOT 发布,相对比较简单没有什么问题。下面让我们试试应用范围最为广泛的 http://ASP.NET CORE 项目 AOT 发布行不行。新建一个 http://ASP.NET CORE WebApi 项目,目标框架选择 NET7 。同样的操作编辑 csproj 文件,添加 PublishAot 属性:
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>

    <PublishAot>true</PublishAot>
  </PropertyGroup>

</Project>
同样使用 dotnet cli 命令进行发布:
dotnet publish -r win-x64 -c Release
不同于上面控制台项目的发布,http://ASP.NET CORE 项目的 AOT 发布会出现很多警告信息,暂且忽略。


等到发布完成后,我们看到生成了一个 27MB 大小的 exe 文件。双击运行起来,不得不提一句,这个启动速度真的是肉眼可见的快,双击之后瞬间就启动了。这个就是 AOT 发布最大的优势了。


访问一下默认生成的那个 Action 方法:http://localhost:5000/WeatherForecast/ 成功的输出了天气信息。


序列化的问题

以上通过简单的测试,http://ASP.NET CORE WebApi 项目顺利的跑起来了, 当然他只是一个简单的示例项目,我们生产的项目相比这些要复杂多了。经过更深入的测试,发现现阶段 http://ASP.NET CORE 进行 AOT 发布后有一个比较麻烦的问题,那就是 JSON 序列化。以下代码是默认生成的 WeatherForecastController 的 GET 方法,这个方法是个标准的同步方法,进行 AOT 发布后序列化没有任何问题。
   [HttpGet]
        public WeatherForecast[] Get()
        {
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();
        }
但是如果把代码改成异步,或者说的更直白一点的话,返回值是 Task<T> 类型就会出现问题。比如把上面的代码使用 Task.FromResult 改造一下,使返回值变成 Task<WeatherForecast[]>
       [HttpGet]
        public async Task<WeatherForecast[]> Get()
        {
            var arr = Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();

            var result = await Task.FromResult(arr);

            return result;
        }
改造的程序进行 AOT 发布后运行,访问对应的接口程序不会有任何报错,但是返回值是个空对象的json:
{}
尝试修复该问题,并没有特别的好办法,目前能够勉强使用的办法是使用 System.Text.Json source generator 模式进行序列化:首先编写一个 WeatherForecastContext 类继承 JsonSerializerContext,并且标记为 partial。为啥要标记为 partial ?因为类的另外部分是 source generator 自动生成的。
   [JsonSerializable(typeof(Task<WeatherForecast[]>))]
    internal partial class WeatherForecastContext : JsonSerializerContext
    {
    }
第二步,在配置 services 的时候顺便把 WeatherForecastContext 配置进去。
builder.Services.AddControllers()
    .AddJsonOptions(options => options.JsonSerializerOptions.AddContext<WeatherForecastContext>());
通过以上操作,再次 AOT 发布后运行程序,访问接口,数据是能正确的返回了。但是有一点小瑕疵是Task对象自身的属性也被序列化出来了。
{
    "result": [
        {
            "date": "2022-10-08T19:14:26.1801524+08:00",
            "temperatureC": 6,
            "temperatureF": 42,
            "summary": "Warm"
        },
        {
            "date": "2022-10-09T19:14:26.1816645+08:00",
            "temperatureC": -9,
            "temperatureF": 16,
            "summary": "Bracing"
        },
        {
            "date": "2022-10-10T19:14:26.1816648+08:00",
            "temperatureC": -1,
            "temperatureF": 31,
            "summary": "Sweltering"
        },
        {
            "date": "2022-10-11T19:14:26.181665+08:00",
            "temperatureC": -17,
            "temperatureF": 2,
            "summary": "Balmy"
        },
        {
            "date": "2022-10-12T19:14:26.1816651+08:00",
            "temperatureC": -16,
            "temperatureF": 4,
            "summary": "Freezing"
        }
    ],
    "asyncState": null,
    "creationOptions": 0,
    "exception": null,
    "id": 1,
    "isCanceled": false,
    "isCompleted": true,
    "isCompletedSuccessfully": true,
    "isFaulted": false,
    "status": 5
}
桌面程序

以上对控制台程序,web 程序进行了测试,接下来顺便对桌面 GUI 程序测试一下吧。


很遗憾,不管是 WINFROM 还是 WPF 程序,进行 AOT 发布的时候直接都会报错,提示不支持。
一些其他限制
AOT 发布的程序会有一些限制,我们编写的时候需要注意:
1、No dynamic loading (for example, Assembly.LoadFile)
2、No runtime code generation (for example, System.Reflection.Emit)
3、No C++/CLI
4、No built-in COM (only applies to Windows)
5、Requires trimming, which has limitations
6、Implies compilation into a single file, which has known incompatibilities
7、Apps include required runtime libraries (just like self-contained apps, increasing their size, as compared to framework-dependent apps)
以上是直接复制的英文文档(原文地址在文末),因为英文不是很好,不进行翻译了,怕误导大家。主要需要注意的就是 1,2 两点 ,关于动态加载类库跟动态生成代码的问题。我想序列化的问题大概也就是出在这里,因为传统的序列化需要大量的使用动态生成代码技术。
总结
通过以上我们对 .NET 上最常用的几种程序进行了 Native AOT 发布的测试。总体来说控制台跟http://ASP.NET CORE 项目能用,WINFROM 跟 WPF 不能用。比较遗憾的有两个点:
1、http://ASP.NET COER 在序列化方面貌似还有点小问题。不知道是不是我环境的问题,如果有知道的大神请指点指点
2、不支持桌面 GUI 程序。其实从个人的经验来说,桌面端可能对启动速度更加敏感一点,因为c/s程序经常性的打开关闭、打开关闭,如果启动慢用户是很容易察觉的。如果桌面程序能支持 AOT ,那么能大大改进现在 .NET 程序的启动速度,这对用户体验的提升是非常大的。服务端的话本身启动一次后就长期运行,用户不会时时刻刻感受到启动速度带来的影响。另外现在 .NET 程序启动本身就不慢,况且还有 R2R 可以选,正常在100-200ms之间的启动速度已经对用户体验影响不大了。所以 AOT 之后的启动速度的优势不是很大。
另外来说说性能,有同学可能觉得 Native AOT 之后性能会有很大的提升,毕竟大家都迷信 Native 速度快嘛。但是经过大佬们的测试事实上 AOT 之后跟没有 AOT 的代码性能基本在伯仲之间,有些地方甚至不如非 Native 的代码。为什么?因为非 Native 代码可以进行运行时 JIT 啊,可以在运行时分析代码对热点代码进行二次 JIT 来提升性能,而 Native AOT 之后的代码做不到这点。
临时加更干货分享

大家能看到这里,已是对我们的支持了。分享一组9月录制的C#零基础教程。我们喜欢做这样的分享,它足够的基础,对新手友好。如果需要的话,就来免费领取吧!



快来领取吧

资料免费自取:

由于内容过多不便呈现,需要视频教程和配套源码的小伙伴,可点击这里,添加我知乎主页个人说明处号码 免费分享
也可直接点击下方卡片:点击后自动复制威芯号,并跳转到威芯。搜索威芯号添加,内容已做打包,备注知乎
即可免费领取,注意查收!
兴致上来了做了张图,也是干货清单,需要的小伙伴直接来领就是了。

包含VS2022 / C#基础 .NET6/WPF/Winform零基础到各类实战!

回复

使用道具 举报

2

主题

8

帖子

13

积分

新手上路

Rank: 1

积分
13
发表于 3 天前 | 显示全部楼层
楼主呀,,,您太有才了。。。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表