16. Jun 2025
Lesedauer 7 Min.
Stark beschränkt
Native-AOT-Kompilierung mit Entity Framework Core 9.0
In Entity Framework Core 9.0 liefert Microsoft erstmals eine Unterstützung für den Native-AOT-Compiler mit, die aber leider noch geringen praktischen Nutzen hat.

Die Datenzugriffskolumne in der dotnetpro-Ausgabe 11/2024 thematisierte die Neuerungen in Entity Framework Core 9.0 unter dem Titel „AOT noch in Arbeit“ [1], denn in der Preview-6-Version, die Basis für diesen Beitrag war, gab es noch kein fassbares Ergebnis zur AOT-Kompilierung in Microsofts objektrelationalem Mapper.Bis zur endgültigen Version, die am 12. November 2024 erschienen ist, hat sich noch etwas getan. Dieser Beitrag betrachtet kritisch das Ergebnis.
Native-AOT-Compiler seit .NET 7.0
Den Just-in-Time-Compiler für .NET gibt es seit .NET Framework 1.0; er erzeugt erst zur Laufzeit den Maschinencode aus der Intermediate Language. Danach gab es immer wieder auch Möglichkeiten, direkt zur Entwicklungszeit Maschinencode zu erzeugen: den Native Image Generator für .NET Framework (NGen), .NET Native für Universal Windows Apps, IL2CPP (Intermediate Language To C++) für Unity, den AOT-Compiler für die Apple-Betriebssysteme und Android in Mono/Xamarin sowie ReadyToRun (R2R) im modernen .NET.Seit .NET 7.0 liefert Microsoft einen neuen Ahead-of-Time-Compiler (AOT), der es erlaubt, .NET-Anwendungen komplett in Maschinencode ohne Just-in-Time-Kompilierung zur Laufzeit auszuliefern. Im Gegensatz zu ReadyToRun (R2R), das nur teilweise vorkompiliert, erzeugt der neue „Native AOT“-Compiler wirklich native, eigenständige Maschinencodes ohne Just-in-Time-Kompilierung. In .NET 7.0 war Native AOT jedoch zunächst nur für Konsolenanwendungen verfügbar.Seit .NET 8.0 sind nun zusätzlich auch folgende Anwendungsarten beim AOT-Compiler möglich: Hintergrunddienste (Worker Services), gRPC-Dienste und Web-APIs. Bei den Web-APIs ist jedoch lediglich das „Minimal Web API“ genannte Modell möglich – mit JSON-Serialisierung via System.Text.Json im Source-Generator-Modus und zunächst ohne Metadaten in der OpenAPI Specification.Neuerungen für Native AOT in .NET 9.0
In .NET 9.0 sind folgende Erweiterungen des AOT-Compilers enthalten:- ASP.NET-Core-SignalR-Hubs können mit Native AOT kompiliert werden.
- .NET MAUI für iOS und macOS: Die zuvor in .NET 8.0 eingeführte experimentelle Unterstützung gilt nun als einsatzklar. Das Trimming wurde verbessert: Bisher gab es nur ein Trimming von Basisklassen sowie Android- und iOS-APIs. Nun gibt es auch ein Trimming des eigenen Codes der Entwickler:innen sowie von Drittanbieterbibliotheken.
- OpenAPI Specification (OAS) in ASP.NET Core Minimal Web APIs.
- AOT für WinUI-3-Oberflächen seit Windows-App-SDK 1.6.
- [FeatureSwitchDefinition] und [FeatureGuardAttribute] für das Entfernen von Codes bei Trimming/AOT.
Vor- und Nachteile des AOT-Compilers
Die Native-AOT-Kompilierung ermöglicht einen deutlich schnelleren Anwendungsstart und kleinere Deployment-Pakete. Auf der Negativseite ist jedoch eine leicht schlechtere Performance bei der Ausführungszeit zu verzeichnen, weil es keinen Just-in-Time-Compiler mehr gibt, der auf dem Zielsystem für dieses optimieren kann.Schwerer wiegt aber bei den Nachteilen, dass einige Funktionen nicht mehr verfügbar sind, die Laufzeitkompilierung voraussetzen. Dazu gehören:- Laufzeitcodegenerierung mit Reflection Emit,
- dynamisches Nachladen von Assemblies für Add-ins/Plug-ins,
- Component Object Model (COM),
- Windows-Runtime-APIs (WinRT),
- Windows Management Instrumentation (WMI),
- Zugriff auf Active Directory Services,
- C++ / CLI,
- AOT mit Web-APIs in den Internet Information Services (IIS),
- die meisten anderen OR-Mapper wie nHibernate und PetaPoco,
- JSON-Serialisierung mit JSON.NET (Newtonsoft JSON),
- AutoMapper und viele andere Drittanbieterkomponenten, die eine der oben genannten Funktionen nutzen.
Native AOT mit Entity Framework Core
Microsoft hatte Native-AOT-Unterstützung für Entity Framework Core Version 8.0 schon auf dem Plan [2]. Aber erst in Entity Framework Core 9.0 gibt es einen ersten, doch leider minimalen Fortschritt, siehe [3]. Vieles ist allerdings weiterhin offen, siehe [4]. In der Dokumentation unter [5] spricht Microsoft daher eine klare Warnung gegen den Produktionseinsatz von Entity Framework Core 9.0 mit dem Native-AOT-Compiler aus (Bild 1).
Aussage von Microsoft zur Native-AOT-Kompilierung in Entity Framework Core 9.0 (Bild 1)
Autor
AOT-Herausforderungen bei Entity Framework Core
Damit Entity Framework Core mit dem Native-AOT-Compiler funktionieren kann, muss die in Entity Framework Core verwendete Laufzeitkompilierung an zwei Stellen durch Codegenerierung zur Entwicklungszeit ersetzt werden:- die Modellkompilierung bei der ersten Verwendung der Kontextklasse in einem Prozess,
- die LINQ- beziehungsweise SQL-Abfragekompilierung beim ersten Aufruf einer Abfrage.
Abfragekompilierung zur Entwicklungszeit
Für die LINQ- oder SQL-Abfragekompilierung bietet Microsoft in Entity Framework Core 9.0 leider nur einen kleinen ersten Schritt. Die Abfragekompilierung für die Native-AOT-Unterstützung in Entity Framework Core 9.0 basiert auf statischer Codeanalyse, bei der LINQ-Befehle im Programmcode gefunden werden. Für diese Abfragen wird dann zur Entwicklungszeit Programmcode generiert und beim Kompilieren im Programmcode via C#-Interceptoren eingesetzt.Entwicklungszeitkompilierung ist durch die statische Codeanalyse bisher leider nur möglich für statische LINQ-Abfragen in Methodensyntax, die sofort mit ToList(), ToArray() und ToHashSet() materialisiert werden:
var blogs = ctx.Websites.Include(b => b.News).Where(
x => x.Id > 42).ToList();
Sämtliche in der Tabelle 1 genannten Abfragen lassen sich hingegen leider nicht zur Entwicklungszeit kompilieren.
Tabelle 1: In diesen Fällen versagt Entity Framework Core 9.0 bei der Native-AOT-Kompilierung
|
Aktivierung der Native-AOT-Kompilierung
Zur Aktivierung der Native-AOT-Kompilierung verwendet man, wie beim Native-AOT-Compiler üblich, folgende Projekteinstellung:
<PropertyGroup>
<PublishAot>true
</PublishAot>
</PropertyGroup>
Zudem muss man die generierten Interceptoren in der Projektdatei so aktivieren:
<PropertyGroup>
<InterceptorsNamespaces>$(InterceptorsNamespaces);
Microsoft.EntityFrameworkCore.GeneratedInterceptors
</InterceptorsNamespaces>
</PropertyGroup>
Nun geht es daran, das Model und die Abfragen zu kompilieren:
dotnet ef dbcontext optimize --precompile-queries
--nativeaot
Die Ausgabe ist in Bild 2 gezeigt.

Ausgabe bei der Abfragekompilierung zur Entwicklungszeit (Bild 2)
Autor
Unter der Voraussetzung, dass die Kontextklasse und alle Abfragen im gleichen Projekt liegen, entstehen dort zwei Ordner mit generiertem Code (siehe Bild 3):

Generierte Dateien, unter der Voraussetzung, dass Kontextklasse und alle Abfragen in verschiedenen Projekten liegen (Bild 3)
Autor
- CompiledModels: das kompilierte Modell mit viel C#-Code;
- Generated: die kompilierten Abfragen mit SQL und viel
C#-Code.
dotnet ef dbcontext optimize --precompile-queries
-–nativeaot
In dem Projekt mit den Abfragen gilt es, im Code einen Verweis auf die Kontextklasse zu ergänzen:
[assembly: DbContext(typeof(Context))]
Danach führt man folgenden Kommandozeilenbefehl in dem Projekt mit den Abfragen aus:
dotnet ef dbcontext optimize --precompile-queries
--nativeaot -c *
Diese Kommandozeilenbefehle müssen nach jeder Änderung am Modell oder den Abfragen immer wieder ausgeführt werden. Die zuvor schon generierten Ordner CompiledModels und Generated sollte man vorher löschen.Beim Veröffentlichen der Anwendung mit dotnet publish sieht man dann Warnungen wie „EF Core isn’t fully compatible with NativeAOT, and running the application may generate unexpected runtime failures.“
Aktivierung von Precompiled Queries ohne Native AOT
Precompiled Queries kann man auch ohne Native AOT einsetzen, indem man zur Entwicklungszeit folgenden Kommandozeilenbefehl ausführt:
dotnet ef dbcontext optimize --precompile-queries
Dies kann die Laufzeitgeschwindigkeit einer Anwendung erhöhen. Es gelten aber die gleichen Einschränkungen, die weiter oben genannt wurden. Daher ist auch dieses Feature zum Stand Entity Framework Core 9.0 wenig praxistauglich!
Fazit
Die derzeitige Implementierung der vorkompilierten Abfragen in Entity Framework Core 9.0 ist in den meisten Praxisanwendungen noch nicht zu gebrauchen, und damit kann man Microsofts objektrelationalen Mapper weiterhin nicht in Native-AOT-kompilierten Anwendungen nutzen. Will man Entity Framework Core einsetzen, muss man also auf die Native-AOT-Kompilierung verzichten oder Alternativen verwenden (siehe unten). Es gibt aber berechtigte Hoffnung, dass Microsoft in Entity Framework Core 10.0, das im November 2025 erscheinen soll, an dieser Stelle etwas nachlegt.Alternativen zu Entity Framework Core bei Native AOT
Da Entity Framework Core 9.0 in Verbindung mit dem Native-AOT-Compiler also kaum brauchbar ist, müssen Sie, wenn Sie den Native-AOT-Compiler verwenden wollen, die Datenbankzugriffe mit einer der folgenden Techniken realisieren:- DataReader, DataSet und Command-Objekte aus ADO.NET,
- DapperAOT (eine funktionsreduzierte Variante von Dapper) [6],
- Nanorm [7].
using var db = new SqliteConnection(connectionString);
var todos = db.QueryAsync<Todo>("SELECT * FROM Todos");
var todosList = new List<Todo>();
Dabei ist QueryAsync<T> eine Erweiterungsmethode auf der Klasse Microsoft.Data.Sqlite.SqliteConnection.Auch zum Einfügen, Ändern und Löschen von Datensätzen verwendet man die SQL-Syntax in einer Zeichenkette:
var todo = new Todo { Title = title };
var createdTodo = await db.QuerySingleAsync<Todo>("""
INSERT INTO Todos(Title, IsComplete)
Values(@Title, @IsComplete)
RETURNING *
""",
todo.Title.AsDbParameter(),
todo.IsComplete.AsDbParameter());
Damit bietet Nanorm deutlich weniger Datenbankmanagementsystem-Abstraktion, Entwicklungskomfort und Robustheit als Entity Framework Core.
Fussnoten
- Holger Schwichtenberg, AOT noch in Arbeit, dotnetpro 11/2024, Seite 108 ff., http://www.dotnetpro.de/A2411DataAccess
- Microsoft Learn, Plan for Entity Framework Core 8, http://www.dotnetpro.de/SL2506-07DataAccess1
- dotnet/efcore auf GitHub, Issue #29754, [9.0] NativeAOT work, http://www.dotnetpro.de/SL2506-07DataAccess2
- dotnet/efcore auf GitHub, Issue #34446, Future NativeAOT work, http://www.dotnetpro.de/SL2506-07DataAccess3
- Microsoft Learn, What‘s New in EF Core 9, http://www.dotnetpro.de/SL2506-07DataAccess4
- DapperAOT auf GitHub, http://www.dotnetpro.de/SL2506-07DataAccess5
- Nanorm auf GitHub, http://www.dotnetpro.de/SL2506-07DataAccess6
- Dapper auf GitHub, http://www.dotnetpro.de/SL2506-07DataAccess7