应用笔记 · 2023年6月23日

Native AOT and databases – Native AOT下可用的ORM组件

原文:https://codevision.medium.com/native-aot-and-databases-87b26f2fcfc8

补充一下,DapperAOT声称可在有限条件下支持AOT,原来用DAPPER实现的应用甚至无需修改代码,待测。

Updated:

Nanorm 

A tiny data-access helper library for ADO.NET. Trimming and native AOT friendly.

It supports:

A lot of applications communicate with databases at this point of time. That’s undeniable fact. Also most people in .NET world do not use ADO.NET directly and instead of rely on the ORM or ORM-like libraries. Given that Native AOT currently has several limitations, what kind of code can be compiled, I decide to test what libraries can be used with NativeAOT without too much pain.

Setting up tests

In order for test what’s available, I decide that Dapper.Performance.Tests suite would work just fine for this purposes. This collection of benchmarks easy to setup and it has references to a lot of other libraries which can be tested too. I run these tests locally without NativeAOT first, and was more then satisfied how easy it was to run the test. Just follow instructions in the repo https://github.com/StackExchange/Dapper#performance and that’s it. After that I start poking into run tests under NativeAOT.

Since Dapper.Performance.Tests uses manual configuration of run for BenchmarkDotNet I have to modify these lines https://github.com/StackExchange/Dapper/blob/main/benchmarks/Dapper.Tests.Performance/Config.cs#L36-L41

By replacing then with these lines


 

For most projects that would be enough, but unfortunately Microsoft.Data.SqlClient has native DLL in the Nuget package Microsoft.Data.SqlClient.SNI.dll and BenchmarkDotNet has issue where it run executable from current location. See https://github.com/dotnet/BenchmarkDotNet/pull/1679 for context. So right now, you have to checkout BenchmarkDotnet locally and use that version instead of package. Hopefully 0.13.0 would be released soon and it would be less necessary use local copy for running perf tests.

I start with test for following benchmarks which run on .NET 5.

  • Dapper
  • EntityFrameworkCore
  • ServiceStack
  • Linq2Db
  • DevExpress XPO
  • Hand coded ADO.NET access
  • PetaPoco
  • Mighty
  • NHibernate
  • Massive
  • LLBGenPro

After I start adding benchmarks, as usually with NativeAOT I have to provide custom RD.xml file to augment what’s types should be added to compilation. More about that I wrote previously here. Here I hit one more limitation of BenchmarkDotNet, you cannot override RD.xml because it provided by BenchmarkDotNet. Since I already have local version of BenchmarkDotnet so I have to manually modify content of RD.xml from location here https://github.com/dotnet/BenchmarkDotNet/blob/b67cfb45a0b0396c2e97bbe49de78b24b5d1a572/src/BenchmarkDotNet/Toolchains/CoreRt/Generator.cs#L198-L212 Keep this in mind if you decide to reproduce my results or improve them.

After all this was done, I start expanding RD.xml until I manage to cover all frameworks. It was a bit painful, since I have to run benchmarks again and again until they stop producing errors about missing types. Here the actual content of RD.xml which is needed for benchmarks to run.


 

Running tests

Unfortunately, results were not very impressive.

Dapper would likely not work on NativeAOT completely since it emits IL on the fly. One out.

ServiceStack also use dynamic code generation, so second is out too.

EntityFrameworkCore is still not implemented. See https://github.com/dotnet/runtimelab/issues/733. One more cannot work with NativeAOT.

Linq2Db works. Excellent.

DevExpress XPO was working. This is good.

Hand coded ADO.NET access was working also.

PetaPoco uses dynamic IL generation. Cannot compile.

Mighty fails with strange error “System.InvalidOperationException: Unknown database provider: System.Data.SqlClient” even if I explicitly register DbProvider. If somebody know what’s going on would be interesting to hear.

Massive works.

NHibernate also fails with strange error. System.ArgumentException: Column requires a valid DataType. Problem sitting somewhere inside System.Data.SqlClient when you retrieve schema and null passed to DataColumn.DataType, so you would need be wary of that scenarios too.

LLBGenPro with Adapter template group works. I was not able to figure out how to create sample code for SelfServicing group.

Summary

So after tests there 5 libraries was working:

  • Hand written ADO.NET
  • Linq2Db  截至2023.6.29, 2.7k个star
  • DevExpress XPO 截至2023.6.29, 147个star
  • Massive 截至2023.6.29, 1.8k个star
  • LLBLGenPro 貌似不是开源的,299欧元

Two potentially working libraries which I simply cannot make work: Mighty and NHibernate. And unfortunately 2 most popular choices Dapper and EF Core does not working.

Performance comparison

Here the results of running BencmarkDotNet


 

Small promotion

You may be found that there entry SqlMarshal in the benchmarks. This is my project where I use Source generators for generate mapping of objects from DbDataReader in a manner similar to Dapper, but which actually friendly to NativeAOT. If you think that interesting to you, take a look at my project: https://github.com/kant2002/SqlMarshal. Performance characteristics actually the same with Dapper if run on actual .NET so you may use that project if you plan to NativeAOT your project later on.

If you want comparison how it stands against other ORM in regular .NET usage — see results below:


 

Thanks for reading. I think at least this article can help you properly assess current limitations with NativeAOT and database usage, and hopefully simplify your journey porting your application to that platform.