Freigeben über


NativeAOT-Unterstützung und vorkompilierte Abfragen (Experimental)

Warnung

NativeAOT und Abfragevorkompilierung sind ein sehr experimentelles Feature und sind noch nicht für die Produktionsverwendung geeignet. Die unten beschriebene Unterstützung sollte als Infrastruktur für das endgültige Feature betrachtet werden, das wahrscheinlich mit EF 10 veröffentlicht wird. Wir empfehlen Ihnen, mit dem aktuellen Support zu experimentieren und Über Ihre Erfahrungen zu berichten, empfehlen jedoch die Bereitstellung von EF NativeAOT-Anwendungen in der Produktion. Weitere Informationen zu bestimmten bekannten Einschränkungen finden Sie unten.

.NET NativeAOT ermöglicht die Veröffentlichung eigenständiger .NET-Anwendungen, die vorab kompiliert wurden (AOT). Dies bietet die folgenden Vorteile:

  • Deutlich schnellere Anwendungsstartzeit
  • Kleine, eigenständige Binärdateien, die kleinere Speicherbedarfe aufweisen und einfacher bereitzustellen sind
  • Ausführen von Anwendungen in Umgebungen, in denen die Just-in-Time-Kompilierung nicht unterstützt wird

EF-Anwendungen, die mit NativeAOT veröffentlicht wurden, starten viel schneller als die gleichen Anwendungen. Neben den allgemeinen .NET-Startverbesserungen, die NativeAOT bietet (d. h. bei jedem Mal keine JIT-Kompilierung erforderlich), kompiliert EF auch LINQ-Abfragen beim Veröffentlichen der Anwendung, sodass beim Starten keine Verarbeitung erforderlich ist und sql bereits zur sofortigen Ausführung verfügbar ist. Je mehr EF LINQ-Abfragen eine Anwendung im Code hat, desto schneller werden die Startgewinne erwartet.

Veröffentlichen einer EF NativeAOT-Anwendung

Aktivieren Sie zunächst die NativeAOT-Veröffentlichung für Ihr Projekt wie folgt:

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

Die Unterstützung der LINQ-Abfrageausführung unter NativeAOT basiert auf der Abfragevorkompilierung: Dieser Mechanismus identifiziert statisch EF LINQ-Abfragen und generiert C# -Interceptors, die Code zum Ausführen jeder bestimmten Abfrage enthalten. Dies kann die Startzeit Ihrer Anwendung erheblich reduzieren, da die schwere Verarbeitung und Kompilierung Ihrer LINQ-Abfragen in SQL bei jedem Start der Anwendung nicht mehr geschieht. Stattdessen enthält der Interceptor jeder Abfrage das fertige SQL für diese Abfrage sowie optimierten Code zum Materialisieren von Datenbankergebnissen als .NET-Objekte.

C#-Interceptors sind derzeit ein experimentelles Feature und erfordern eine spezielle Opt-In in Ihrer Projektdatei:

<PropertyGroup>
  <InterceptorsNamespaces>$(InterceptorsPreviewNamespaces);Microsoft.EntityFrameworkCore.GeneratedInterceptors</InterceptorsNamespaces>
</PropertyGroup>

Schließlich enthält das Microsoft.EntityFrameworkCore.Tasks Paket DIE MSBuild-Integration, die die Vorkompilierung der Abfrage ausführt (und das erforderliche kompilierte Modell generiert), wenn Sie Ihre Anwendung veröffentlichen:

<ItemGroup>
  <PackageReference Include="Microsoft.EntityFrameworkCore.Tasks" Version="9.0.0">
    <PrivateAssets>all</PrivateAssets>
    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
  </PackageReference>
</ItemGroup>

Sie können jetzt Ihre EF NativeAOT-Anwendung veröffentlichen:

dotnet publish -r linux-arm64 -c Release

Dies zeigt die Veröffentlichung einer NativeAOT-Veröffentlichung für Linux, die auf ARM64 ausgeführt wird; In diesem Katalog finden Sie Ihren Laufzeitbezeichner. Wenn Sie die Interceptors ohne Veröffentlichung generieren möchten , z. B. um die generierten Quellen zu untersuchen, können Sie dies über den dotnet ef dbcontext optimize --precompile-queries --nativeaot Befehl tun.

Aufgrund der Funktionsweise von C#-Interceptors werden alle Änderungen in der Anwendungsquelle ungültig und erfordern eine Wiederholung des obigen Prozesses. Daher wird die Generierung von Interceptor und die tatsächliche Veröffentlichung nicht in der inneren Schleife erwartet, da der Entwickler an Code arbeitet; Stattdessen können beide und dotnet ef dbcontext optimize dotnet publish in einem Veröffentlichungs-/Bereitstellungsworkflow in einem CI/CD-System ausgeführt werden.

Hinweis

Die Veröffentlichung meldet derzeit eine Reihe von Kürzungs- und NativeAOT-Warnungen, was bedeutet, dass Ihre Anwendung nicht vollständig garantiert ist, ordnungsgemäß ausgeführt zu werden. Dies wird aufgrund des aktuellen experimentellen Zustands der NativeAOT-Unterstützung erwartet; Das endgültige, nicht experimentelle Feature meldet keine Warnungen.

Begrenzungen

Dynamische Abfragen werden nicht unterstützt.

Abfragevorkompilierung führt statische Analysen des Quellcodes durch, identifizieren EF LINQ-Abfragen und Generieren von C#-Interceptors für sie. LINQ ermöglicht das Ausdrücken von hoch dynamischen Abfragen, bei denen LINQ-Operatoren basierend auf beliebigen Bedingungen zusammengesetzt werden; Solche Abfragen können leider nicht statisch analysiert werden und werden derzeit nicht unterstützt. Betrachten Sie das folgende Beispiel:

IAsyncEnumerable<Blog> GetBlogs(BlogContext context, bool applyFilter)
{
    IQueryable<Blog> query = context.Blogs.OrderBy(b => b.Id);

    if (applyFilter)
    {
        query = query.Where(b => b.Name != "foo");
    }

    return query.AsAsyncEnumerable();
}

Die oben genannte Abfrage wird auf mehrere Anweisungen aufgeteilt und erstellt den Where Operator dynamisch basierend auf einem externen Parameter. Solche Abfragen können nicht vorkompiliert werden. Es ist jedoch manchmal möglich, solche dynamischen Abfragen wie mehrere nicht dynamische Abfragen neu zu schreiben:

IAsyncEnumerable<Blog> GetBlogs(BlogContext context, bool applyFilter)
    => applyFilter
        ? context.Blogs.OrderBy(b => b.Id).Where(b => b.Name != "foo").AsAsyncEnumerable()
        : context.Blogs.OrderBy(b => b.Id).AsAsyncEnumerable();

Da die beiden Abfragen von Anfang bis Ende statisch analysiert werden können, können sie vorkompiliert werden.

Beachten Sie, dass dynamische Abfragen in Zukunft wahrscheinlich bei Verwendung von NativeAOT unterstützt werden; Da sie jedoch nicht vorkompiliert werden können, verlangsamen sie weiterhin den Anwendungsstart und führen im Allgemeinen im Vergleich zur nicht nativeNAOT-Ausführung weniger effizient aus. Dies liegt daran, dass EF intern auf der Codegenerierung basiert, um Datenbankergebnisse zu materialisieren, die Codegenerierung wird jedoch bei Verwendung von NativeAOT nicht unterstützt.

Weitere Einschränkungen

  • LINQ-Abfrageausdruckssyntax (manchmal als "Verständnisssyntax" bezeichnet) wird nicht unterstützt.
  • Das generierte kompilierte Modell und Abfrage-Interceptors können derzeit ziemlich groß in Bezug auf die Codegröße sein und eine lange Zeit dauern, bis sie generiert werden. Wir planen, dies zu verbessern.
  • EF-Anbieter müssen möglicherweise Unterstützung für vorkompilierte Abfragen erstellen; Überprüfen Sie die Dokumentation Ihres Anbieters, um zu wissen, ob sie mit der NativeAOT-Unterstützung von EF kompatibel ist.
  • Wertkonverter, die den erfassten Zustand verwenden, werden nicht unterstützt.

Vorkompilierte Abfragen ohne NativeAOT

Aufgrund der aktuellen Einschränkungen der NativeAOT-Unterstützung von EF kann es für einige Anwendungen möglicherweise nicht verwendet werden. Möglicherweise können Sie jedoch vorkompilierte Abfragen nutzen, während sie reguläre, nicht nativeAOT-Anwendungen veröffentlichen; Auf diese Weise können Sie zumindest von der Reduzierung der Startzeit profitieren, die vorkompilierte Abfragen anbieten, während sie dynamische Abfragen und andere Features verwenden können, die derzeit nicht mit NativeAOT unterstützt werden.

Die Verwendung von vorkompilierten Abfragen ohne NativeAOT ist einfach eine Frage der Ausführung der folgenden:

dotnet ef dbcontext optimize --precompile-queries

Wie oben gezeigt, generiert dies ein kompiliertes Modell und Interceptors für Abfragen, die vorkompiliert werden können, wodurch der Aufwand aus der Startzeit Ihrer Anwendung entfernt wird.