.NET8.0 AOT 经验分享 - 专项测试各大 ORM 是否支持

AOT 特点

发布和部署本机 AOT 应用具有以下优势:

  • 最大程度减少磁盘占用空间:使用本机 AOT 发布时,将生成一个可执行文件,其中仅包含支持程序所需的外部依赖项的代码。减小的可执行文件大小可能会导致:
    • 较小的容器映像,例如在容器化部署方案中。
    • 缩短了较小映像的部署时间。
  • 缩短启动时间:本机 AOT 应用程序可缩短启动时间,这意味着
    • 应用已准备好更快地为请求提供服务。
    • 改进了容器业务流程协调程序需要管理从应用的一个版本到另一个版本的转换的部署。
  • 减少内存需求:本机 AOT 应用可能会减少内存需求,具体由应用执行的工作决定。 减少内存消耗可以提高部署密度和可伸缩性。

模板应用在基准测试实验室中运行,来比较 AOT 已发布的应用、已修剪的运行时应用和未修剪的运行时应用的性能。下图显示了基准测试的结果:


本文内容

2023年11月15日,对.net的开发圈是一个重大的日子,.net 8.0正式版发布。

那一天,我发表了一篇关于 《.NET8.0 的升级和 AOT 经验的文章》,整体总结如下:

.NET8.0 AOT 已经到了可用的阶段,期待未来版本能改进以下问题:

  • 发布速度变快,目前20-30秒一次实在太慢
  • 编译前检查错误,而不是等发布后再报运行时错误
  • 加强调试,.pdb 100兆++ 为何调试还都是 c++ 有关内容,不能白瞎了这么大的调试文件啊
  • 尽快修复 Console.WriteLine(Enum.GetValues(typeof(TaskInterval))) 这个问题

如果一个全新的 AOT webapi 应用发布在国产系统上运行,算不算国产信创?

我是开源人:https://github.com/2881099

今天发表 AOT 经验的续篇和 ORM 有关,肯定会产生一些火药味,咱们能不能理智用技术的角度看完内容,提示:测试你不是贬低你,OK???

我在 github 上创建一个专门测试 AOT 发布的开源项目,有兴趣可以参与提交代码。

https://github.com/2881099/aot_test


FreeSql v3.2.805 + Sqlite

shell 复制代码
发布耗时 31.882 秒

orm_freesql.exe    ( 16,927KB)
orm_freesql.pdb    (123,812KB)
SQLite.Interop.dll (  1,723KB)

E:\github\aot_test\orm_freesql\bin\Release\net8.0\publish\win-x64>orm_freesql.exe
【FreeSql AOT】开始测试...
Insert 1条 80ms
Select 1条 5ms
Update 1条 86ms
Select 1条 0ms
Delete 1条 74ms
【FreeSql AOT】测试结束.

PS:没有对 AOT 的支持做专门改进,都是老代码。


SqlSugar v5.1.4.117 + Sqlite

shell 复制代码
发布耗时 01:22.813 分钟

orm_sqlsugar.exe                 ( 39,875KB)
orm_sqlsugar.pdb                 (266,084KB)
e_sqlite3                        (  1,597KB)
Microsoft.Data.SqlClient.SNI.dll (    499KB)

E:\github\aot_test\orm_sqlsugar\bin\Release\net8.0\publish\win-x64>orm_sqlsugar.exe
【SqlSugar AOT】开始测试...
Unhandled Exception: System.ArgumentNullException: Value cannot be null. (Parameter 'type')
   at System.ArgumentNullException.Throw(String) + 0x2b
   at System.ActivatorImplementation.CreateInstance(Type, Boolean) + 0xe7
   at SqlSugar.InstanceFactory.NoCacheGetCacheInstance[T](String) + 0x84
   at SqlSugar.InstanceFactory.CreateInstance[T](String) + 0x82
   at SqlSugar.InstanceFactory.GetCodeFirst(ConnectionConfig) + 0x3e
   at SqlSugar.SqlSugarProvider.get_CodeFirst() + 0x1a
   at Program.<Main>$(String[] args) + 0x138
   at orm_sqlsugar!<BaseAddress>+0x11f25d3

上述由 .CodeFirst.InitTables<TaskInfo>() 报错,去掉该代码手工创建表之后,再次发布运行:

shell 复制代码
发布耗时 59.911 秒

E:\github\aot_test\orm_sqlsugar\bin\Release\net8.0\publish\win-x64>orm_sqlsugar.exe
【SqlSugar AOT】开始测试...
Unhandled Exception: System.ArgumentNullException: Value cannot be null. (Parameter 'type')
   at System.ArgumentNullException.Throw(String) + 0x2b
   at System.ActivatorImplementation.CreateInstance(Type, Boolean) + 0xe7
   at SqlSugar.InstanceFactory.NoCacheGetCacheInstance[T](String) + 0x84
   at SqlSugar.InstanceFactory.CreateInstance[T](String) + 0x82
   at SqlSugar.InstanceFactory.GetSqlbuilder(ConnectionConfig) + 0x7a
   at SqlSugar.SqlSugarProvider.CreateInsertable[T](T[]) + 0x39
   at SqlSugar.SqlSugarProvider.Insertable[T](T[] insertObjs) + 0x3b
   at SqlSugar.SqlSugarProvider.Insertable[T](List`1 insertObjs) + 0xb5
   at SqlSugar.SqlSugarClient.Insertable[T](List`1) + 0x44
   at Program.<Main>$(String[] args) + 0x1af
   at orm_sqlsugar!<BaseAddress>+0x11f24f3

这次由 .Insertable 报错,无解?后面还是更难兼容的更新、查询操作没执行~~

https://www.cnblogs.com/sunkaixuan/p/17839825.html

《NET8 ORM 使用AOT SqlSugar posted》 @ 2023-11-17 22:31 果糖大数据科技

这篇文章是是针对 FreeSql 2023-11-16 发的 AOT 文章发的么?

https://www.cnblogs.com/FreeSql/p/17836000.html

《.NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试》 posted @ 2023-11-16 14:18 FreeSql

FreeSql 一年没发文章了,发一次就被针对 seo 关键字,好歹先测试下再发文章啊!~~


EFCore v8.0 + Sqlite

shell 复制代码
发布耗时 50.749 秒

orm_efcore.exe     ( 17,410KB)
orm_efcore.pdb     (168,788KB)
e_sqlite3          (  1,652KB)

E:\github\aot_test\orm_efcore\bin\Release\net8.0\publish\win-x64>orm_efcore.exe
【EFCore AOT】开始测试...
Unhandled Exception: System.InvalidOperationException: Model building is not supported when publishing with NativeAOT. Use a compiled model.
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel(Boolean) + 0x148
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model() + 0x1c
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite, RuntimeResolverContext, ServiceProviderEngineScope, RuntimeResolverLock) + 0xc2
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite, RuntimeResolverContext) + 0x35
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument) + 0xa4
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite, RuntimeResolverContext) + 0x83
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite, RuntimeResolverContext, ServiceProviderEngineScope, RuntimeResolverLock) + 0xc2
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite, RuntimeResolverContext) + 0x35
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument) + 0xa4
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite, RuntimeResolverContext) + 0x83
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite, RuntimeResolverContext, ServiceProviderEngineScope, RuntimeResolverLock) + 0xc2
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite, RuntimeResolverContext) + 0x35
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument) + 0xa4
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite, RuntimeResolverContext) + 0x83
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite, RuntimeResolverContext, ServiceProviderEngineScope, RuntimeResolverLock) + 0xc2
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite, RuntimeResolverContext) + 0x35
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument) + 0xa4
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite, RuntimeResolverContext) + 0x83
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite, RuntimeResolverContext, ServiceProviderEngineScope, RuntimeResolverLock) + 0xc2
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite, RuntimeResolverContext) + 0x35
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument) + 0xa4
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite, RuntimeResolverContext) + 0x83
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite, RuntimeResolverContext, ServiceProviderEngineScope, RuntimeResolverLock) + 0xc2
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite, RuntimeResolverContext) + 0x35
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument) + 0xa4
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite, ServiceProviderEngineScope) + 0x3d
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier, ServiceProviderEngineScope) + 0xa3
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type) + 0x42
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider, Type) + 0x50
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider) + 0x29
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies() + 0x32
   at Microsoft.EntityFrameworkCore.DbContext.get_ContextServices() + 0x14f
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies() + 0x22
   at Microsoft.EntityFrameworkCore.DbContext.EntryWithoutDetectChanges[TEntity](TEntity entity) + 0x18
   at Microsoft.EntityFrameworkCore.DbContext.SetEntityState[TEntity](TEntity entity, EntityState entityState) + 0x1d
   at Program.<Main>$(String[] args) + 0x1da
   at orm_efcore!<BaseAddress>+0x7c2ab0

Dapper

Dapper 已经出了新的 DapperAOT 版本,就不测试了,100%支持 AOT。

写到最后

希望多用技术交流,我写文章不是自夸 Free 系列开源项目有多流弊,实实在在的花了时间研究,测试通过后才发的文章,分享给大家多一个选择,让大家知晓已支持 AOT,仅此而已。

提示:测试你不是贬低你,OK???其他 ORM 可以按 https://github.com/2881099/aot_test 的方式提交测试代码,我确实没时间测试所有 ORM,不是为了比较而比较。

被抢 SEO 关键字行为已经不是一次两次了,确实没多大意思。

最后上一个统计表格吧,PS:支持 AOT 没什么了不起!

测试项目 发布耗时 发布后 .exe 体积 发布后 .pdb 体积 通过AOT
FreeSql v3.2.805 + Sqlite 31.882 秒 16,927KB 123,812KB 通过
SqlSugar v5.1.4.117 + Sqlite 82.813 秒 39,875KB 266,084KB 未通过
EFCore v8.0 + Sqlite 50.749 17,410KB 168,788KB 未通过
DapperAOT 未测试(支持) 未测试(支持) 未测试(支持) 通过

如果大家对 AOT 有兴趣,我后面会持续分享自己的经验,PS mysql 测试也是没问题的,其他数据库如果有使用问题可以与我交流。

我是开源人:https://github.com/2881099