Back to "Multiplatform AOT mit SQLite-:, wie man es funktioniert"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

.NET AOT DevOps SQLite

Multiplatform AOT mit SQLite-:, wie man es funktioniert

Tuesday, 16 December 2025

Einleitung

Native AOT verspricht, Ihre .-NET-Anwendungen in winzige , self-- integrierte Executables umzuwandeln, die sofort starten und überall laufen lassen.—Keine Laufzeitinstallation erforderlich. DllNotFoundException Abstürze kämpfen mit transitiven Abhängigkeiten und fragen sich, warum PublishSingleFile=true doesns-' bündelt tatsächlich alles in eine einzelne Datei.. , Dies ist die komplette , , Anfänger ,- , zu erhalten SQLite Arbeit mit Native AOT , , , Gebäude für mehrere Plattformen , und Automatisierung von Releases mit GitHub Actions , . , Bis zum Ende haben ' , Sie haben Binärdateien , die unter Windows laufen , unter , , Linux , einschließlich Raspberry Pi...

Was ist AOT.? und warum sollten Sie sich darum kümmern?

Lassen Sie die 's mit den absoluten Basics beginnen Wenn du jemals eine . NET-Anwendung gebaut hast und dich gefragt hast, warum du die ". NET Laufzeit auf Servern installieren musst oder warum deine Konsolen-App eine Sekunde dauert, um das erste Mal zu starten, wenn du es ausführst.,.AOT ist die Antwort auf diese Probleme.

Wie .-NET normalerweise funktioniert (JIT Compilation

Wenn du den Code schreibst und deine Anwendung erstellst, dann produziert der Compiler doesns-'.t Maschinencode, den deine CPU direkt ausführen kann, stattdessen erzeugt es etwas namens ,. Mittlere SpracheDenken Sie daran, wie auf halbem Weg zwischen Ihrem Code und den eigentlichen Maschinenanweisungen.

Wenn du deine .-NET-App hier ausführst, was passiert

  1. Ihr Computer lädt das separate Programm (
  2. Die Laufzeit liest Ihren IL-Code
  3. Nur in der Zeit von (JIT-) compiler konvertiert IL in nativen Maschinencode, während Ihre App läuft
  4. Ihre CPU führt endlich diesen Maschinencode aus

Dies ist wie mit einem Übersetzer, der Ihr Rezept liest (.IL.) und übersetzt es mündlich zu einem Küchenchef übersetzt.

  • Die Runtime von -. NET ist von zusätzlichen Dateien, die Sie bereitstellen müssen
  • Der JIT-Compiler braucht Zeit, um Code zu übersetzen.
  • Der JIT Compiler selbst sitzt im Speicher, während Ihre App läuft

Geben Sie AOT (Ahead--Of ---Zeitkompilierung ein

Native AOT kippt dieses Modell auf seinen Kopf, statt Ihren Code zur Laufzeit zu übersetzen, es übersetzt alles zur BauzeitSie enden mit einer einzigen ausführbaren Datei, die tatsächlichen Maschinencode enthält, den Ihre CPU direkt ausführen kann.

Denken Sie daran, wie immer ein professionell übersetztes Rezept Buch statt der Einstellung eines Live-Übersetzers Die Arbeit wird einmal getan ., ?upfront ?, ? und das Ergebnis ist sofort einsatzbereit

Die Vorteile des Spiels

Hier ist, was Native AOT Ihnen gibt.

Ausführbare kleine Dateien:, MKS1MB statt 150, MMS3, MWS3

Ihre App und alles, was es braucht, wird in eine kleine binäre kompiliert... Keine separaten Laufzeitdateien...

Sofortige InbetriebnahmeSchnellere Kaltstarts

Meine Tests...:... normal.... NET nahm ~800 ms, um zu starten., - AOT nahm ~150 ms. - There...'... kein JIT warm...- up time...MSC8...der Maschinencode ist sofort fertig.

Keine AbhängigkeitenKeine Laufzeit erforderlich

Sie können Ihre ausführbare Maschine auf eine beliebige Maschine kopieren, mit dem richtigen OS (.Windows./.Linux./.Mac...). und es läuft einfach.MS K4. Nein.MSC5.install.

Untere SpeichernutzungMehr als mehr als weniger Speicher

Kein JIT-Compiler, der im Speicher sitzt... in meiner Gateway-Anwendung... normales .-NET benutzte .85-MB im Leerlauf... verwendete AOT...

Besser für eingeschränkte UmgebungenArbeiten, bei denen JIT

Einige Umgebungen – sichere Docker-Container – iOS – eingebettete Systeme – ) – don-' – erlauben die Erzeugung von Laufzeitcodes – . – AOT funktioniert überall

Die Trade- ---offs - (-There immer ein Catch-)

AOT ist ,' ,t magic , . , You , ' ,re Trading-Laufzeitflexibilität für die Upfront-Optimierung

Keine dynamische Codegenerierung

Alles, was Code zur Laufzeit erzeugt won-'-t work-:-

  • System.Reflection.Emit Arten dynamisch erzeugend
  • Dynamische Beladung der Montage DLLs zur Laufzeit
  • Einige ausgefallene Reflexionstricks

Der meiste normale Code ist fein, aber einige Frameworks, die stark auf Reflexion angewiesen sind, benötigen eine spezielle Konfiguration.

Plattform-®spezifische Builds

JIT-Compilation produziert IL, die auf jeder Plattform läuft. eine spezifische PlattformSie müssen separat für das Bauen bauen.

  • Fenster x-64-
  • Linux x.64.
  • Linux-ARM-64Raspberry-Pi-)
  • macOS Intel
  • macOS Apple Silicon

Wir automatisieren dies mit GitHub Aktionen später

Längere Bauzeiten

Statt IL in Sekunden zu kompilieren, kompiliert AOT den ganzen Weg zum Maschinencode.Erwarten Sie Minuten anstelle von Sekunden.. ist ein einmaliger Kostenfaktor für dauerhafte Vorteile.

Einige Funktionen benötigen eine zusätzliche Konfiguration

JSON-Serialisierung-,-Entity Framework und alles, was schwere Reflexion verwendet möglicherweise müssen Sie ausdrücklich sagen, die Compiler, welche Arten zu halten ?....

Wann sollten Sie AOT-? verwenden?

Native AOT ist perfekt für die

  • CLI-WerkzeugeBefehlszeilen-Dienstprogramme, bei denen die sofortige Inbetriebnahme von Bedeutung ist
  • MikrodiensteSkalierung in Kubernetes schneller
  • Serverlos, /-LambdaKalte Startzeit wirkt direkt auf Ihre Rechnung
  • KanteneinrichtungenIoT-Geräte mit begrenzten Ressourcen
  • Gateway-AnwendungenAPI-Gateways mit hohem Durchsatz
  • Desktop-WerkzeugeVerschiffen Sie ein einzelnes .exe ohne Installationsschritt

AOT für„:" überspringen

  • Traditionelle Web-Apps, bei denen Startzeit nicht zählt
  • Apps mit schweren Entity Framework mit vielen Migrationen
  • Plugin-Systeme, die DLLs dynamisch laden
  • Alles, was Code zur Laufzeit erzeugt

Für CLI-Tools, ,, Microservices, ,, Container und Kantengeräte sind die Verbesserungen Spiel.-, das Ändern von . Aber da ist ein Haken, wenn Sie Datenbanken hinzufügen, MSC7, speziell SQLite-.,

Das SQLite-Problem

Jetzt, da Sie verstehen, was AOT ist let-'s sprechen über den einzigen größten Schmerzpunkt , : , SQLite ,. , Dies ist die Wand die meisten Menschen getroffen, wenn sie versuchen, AOT mit Datenbank verwenden , - , unterstützte Anwendungen ..

Was passiert mit der Frustrierenden Erfahrung?

Hier ist die typische Reise.

  1. Sie fügen hinzu: Microsoft.Data.Sqlite zu Ihrem Projekt
  2. Sie konfigurieren PublishAot=true in Ihrem .csproj
  3. Das Build komplettiert ohne Fehler alles sieht gut aus.
  4. Sie führen die ausführbare Datei aus und erhalten sofort
DllNotFoundException: Unable to load DLL 'e_sqlite3' or one of its dependencies

Deine App stürzt ab, bevor sie alles tun kann.

Native Bibliotheken verstehen

Um das Problem zu verstehen, müssen Sie wissen über native Bibliotheken.

SQLite ist ,' , geschrieben in , . , NET , — , in C , . , es ,' , kompiliert zu Plattform , E- , spezifischen nativen Code ,

  • e_sqlite3.dll unter Windows
  • libe_sqlite3.so unter Linux
  • libe_sqlite3.dylib auf macOS

Wenn Sie Microsoft.Data.Sqlite in der normalen Version von .-NET-,.' ist nur ein Wrapper um diese native SQLite-Bibliothek herum... Zur Laufzeit versucht es dynamisch laden die entsprechende native Datei für Ihre Plattform

Dies funktioniert in Ordnung mit normalem .-NET, weil

  1. Die Laufzeit kann Bibliotheken dynamisch laden
  2. NuGet-Pakete können native Bibliotheken für alle Plattformen enthalten
  3. Die richtige wird zur Laufzeit ausgewählt

Warum AOT dies bricht

Native AOT hat zwei Eigenschaften, die mit SQLite kollidieren.

Aggressive BeschneidungWenn es statisch beweisen kann, dass Sie etwas benötigen, wird es gelöscht..... Dynamische Bibliothek Laden verwirrt den Trimmer... ..' Sehen Sie die Verbindung zwischen Ihrem Code und der nativen SQLite DLL..

Keine dynamische BelastungsunterstützungEs erwartet, dass alle nativen Abhängigkeiten zur Kompilierungszeit explizit verknüpft werden.

Das Ergebnis Microsoft.Data.Sqlite erwartet, eine native SQLite-Bibliothek zur Laufzeit zu finden.,, aber AOT hat sie entweder weggetrimmt oder weiß nicht, wie man sie richtig bündelt

Der Transitive Dependency Nightmare

Noch schlimmer, wenn Sie andere NuGet-Pakete haben, die SQLite verwenden, wie einige ORM-Bibliotheken oder meine mostlylucid.ephemeral.complete Paket-), , können Sie am Ende mit mehrere inkompatible SQLite-Anbieter in Ihrem Abhängigkeitsbaum

Jeder Anbieter versucht anders zu arbeiten.

  • Man könnte erwarten, dass OS--, vorausgesetzt SQLite nur ), Windows
  • Ein anderer könnte seine eigene SQLite bündeln
  • Ein anderer könnte eine andere native Bibliotheksversion verwenden

Der AOT-Compiler wird verwirrt, über welche man zu verwenden ist, und oft das Ergebnis ist, dass es keine von ihnen enthält.

Die Lösung verwenden Sie das Bündel

Die Lösung ist: SQLitePCLRaw.bundle_e_sqlite3Ein spezielles NuGet-Paket, das speziell entwickelt wurde, um mit AOT-. zu arbeiten.

Fügen Sie diese beiden Pakete zu Ihrem Projekt hinzu

<ItemGroup>
  <PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
  <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.10" />
</ItemGroup>

Was macht der Bundle?

Das bundle_e_sqlite3 Paket unterscheidet sich von normalen SQLite-Providern.

Es enthält vor-angepasste SQLite-Bibliotheken für jede große Plattform.

  • Fenster mit einer Breite von nicht mehr als 2 mm
  • Linux-Distros wie Alpine, )
  • macOS (-x-64-Intel.,-ARM, M64-Apfel-Silicium

Diese nativen Bibliotheken sind in einer Weise verpackt, die der AOT Compiler versteht

Das Bundle markiert seine nativen Abhängigkeiten explizit, so dass der AOT-Compiler weiß, sie in die finale Binärdatei einzubinden.

Es wurde für cross-Versionen von - entwickelt

Ein Paket funktioniert für alle Plattformen ,. , Sie brauchen nicht Plattform , - , spezifische Pakete oder bedingte Referenzen ..

Warum das funktioniert

Erinnern Sie sich an die zwei Probleme, die wir identifizierten

Problem-Beschneidung entfernt Bibliotheken kann es verwendet werden

  • Lösung: :-Das Bundle erklärt explizit seine nativen Bibliotheken als Build-Assets, die enthalten sein müssen

Problem 2:-AOT unterstützt dynamisches Laden

  • Lösung: :-Das Bundle verknüpft die SQLite Bibliotheken statisch zur Kompilierungszeit

Das Ergebnis, wenn Sie für Windows x.64, Build das Bundle enthält e_sqlite3.dllWenn Sie für Linux ARM.64, Build es enthält die ARM... libe_sqlite3.soAlles, was gerade funktioniert

Kritisch ,: , Initialisieren Sie den Bundle , ( , Don , ' , Überspringen Sie dies ,!) ,

Hier ist der Teil, der nach oben stöbert.90%' von Leuten, die versuchen SQLite mit AOT-,' zu benutzen, inklusive mir bei meinem ersten Versuch.MSC3'

mit normalem .-NETDas SQLite-Bundle initialisiert sich automatisch, wenn Sie SQLite verwenden.

Mit Native AOTDiese automatische Initialisierung doesnts ,' work , . , Der AOT-Compiler can , ' , siehe den automatischen Startcode ,( , es sieht aus wie ungenutzter Code und wird getrimmt , so dass Sie muss manuell initialisiert werden das Bundle am Anfang Ihrer Anwendung

Hier ist die magische Linie, die du brauchst.

using SQLitePCL;

public class Program
{
    public static void Main(string[] args)
    {
        // THIS IS CRITICAL: Initialize SQLite FIRST, before ANYTHING else
        SQLitePCL.Batteries.Init();

        // Now you can do normal application setup
        var builder = WebApplication.CreateBuilder(args);

        // This is now safe - SQLite is initialized
        builder.Services.AddDbContext<MyDbContext>(options =>
            options.UseSqlite("Data Source=app.db"));

        var app = builder.Build();
        app.Run();
    }
}

Was tut Batteries.Init() Behandeln Sie:

Diese Methode sagt das SQLite-Bundle zu.

  1. Finden Sie die richtige native SQLite-Bibliothek für Ihre aktuelle Plattform
  2. Laden Sie es in den Speicher
  3. Verkabeln Sie alle Verbindungen zwischen Microsoft.Data.Sqlite und die Heimatbibliothek

Es ist ', genannt ".Batterien, weil es ', die Batterien enthalten ", Bündel —, alles was Sie brauchen, wird zusammen verpackt.

Wo soll man es hinstellen?

Setzen Batteries.Init() als die erste Zeile in Ihrem Main Methode vor der Behandlung mit :

  • Erstellen des Anwendungs-Builders
  • Abhängigkeits-Injektion konfigurieren
  • Öffnen von Datenbankverbindungen
  • Konfigurationsdateien lesen, die SQLite verwenden können

Denken Sie daran, wie das Einstecken eines Geräts vor dem Versuch, es einzuschalten ?.? Wenn Sie versuchen, SQLite vor dem Aufruf verwenden Init()Sie erhalten immer noch die DllNotFoundException auch wenn die DLL korrekt in Ihrer Anwendung gebündelt ist

Was passiert, wenn Sie vergessen?

Wenn Sie vergessen zu rufen Batteries.Init()Ihre App wird von :

  1. Mit Erfolg Builden Sie den Compiler Won , ' , warnt Sie nicht
  2. Starten Sie
  3. Absturz in dem Moment, in dem es versucht, SQLite mit zu verwenden.
DllNotFoundException: Unable to load DLL 'e_sqlite3' or one of its dependencies

Dies ist verwirrend, weil die DLL wird gebündelt in Ihrer Anwendung, nur nicht initialisiert

Vollständige Projektkonfiguration

Hier ist ein vollständiges .csproj konfiguriert für Multi---Plattform Native AOT mit SQLite-:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>

    <!-- Native AOT -->
    <PublishAot>true</PublishAot>
    <PublishTrimmed>true</PublishTrimmed>
    <TrimMode>full</TrimMode>
    <InvariantGlobalization>true</InvariantGlobalization>

    <!-- Single File -->
    <PublishSingleFile>true</PublishSingleFile>
    <StripSymbols>true</StripSymbols>

    <!-- Optimization -->
    <OptimizationPreference>Speed</OptimizationPreference>

    <!-- Multi-platform targets -->
    <RuntimeIdentifiers>
      win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64
    </RuntimeIdentifiers>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
    <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.10" />
  </ItemGroup>

</Project>

Schlüssel-Einstellungen erklärt im Klartext

Lassen Sie mich nach unten brechen, was jede dieser Einstellungen tut.

PublishAot=true

Dies ist der Master-Schalter, der Native AOT Compilation ermöglicht. ohne diese ,, erhalten Sie normales Verhalten von ., JIT compilation, )., mit diesem , erhalten Sie voran.

PublishTrimmed=true und TrimMode=full

Diese sagen dem Compiler, um jeden beliebigen Code zu entfernen you-'e nicht verwenden ?.?Denken Sie daran, wie das Reinigen Ihrer Garage vor dem Verschieben ?— ?warum Pack Dinge, die Sie brauchen nicht

  • PublishTrimmed=true ermöglicht das Beschneiden
  • TrimMode=full bedeutet, dass Sie alles entfernen, was Sie können

WarnungDieses kann Code brechen, der schwere Reflexion verwendet...... wie einige JSON Serializer oder OR)... weil der Trimmer immer sehen kann, was du über reflection.......

InvariantGlobalization=true

Dies entfernt alle Kultur- ,--spezifische Daten aus Ihrer App , — , Datumsformate , , , Währungssymbole , , , Textsortierungsregeln für verschiedene Sprachen , E. , speichert 5-10 , MB , und . ,

Nur die Einstellung auf true wenn Ihre App

  • Nur Englisch
  • Braucht Kultur nicht?
  • Nur Verwendungen von Ordinal-Vergleichen mit (,-byte,--,- von -,-Byte und -)

Wenn Sie ein CLI-Tool oder API-Gateway bauen, das sich nicht um die Lokalisierung kümmert, ist es kostenlos zu sparen, wenn Sie etwas aufbauen, das Daten für französische Benutzer formatieren muss, oder Türkischen Text korrekt sortieren.

PublishSingleFile=true

Verbindet alles in eine ausführbare Datei.

myapp.exe
myapp.dll
System.Text.Json.dll
... 50 more files

Du erhältst einfach

myapp.exe

Viel einfacher zu implementieren

StripSymbols=true

Debug-Symbole helfen Debuggern, Ihnen variable Namen und Zeilennummern beim Debuggen zu zeigen..-Sie sind während der Entwicklung nützlich, fügen aber mehrere Megabyte zu Ihrer letzten Binärdatei hinzu.

Diese Einstellung entfernt sie... Ihre App läuft genau die gleiche...

OptimizationPreference=Speed

Dies sagt dem Compiler, was zu priorisieren ist, wenn Entscheidungen getroffen werden.

  • SpeedMachen Sie es schnell - leicht größere Binaries - aber bessere Leistung
  • SizeMachen Sie es klein - leicht langsamer - aber minimale binäre Größe -

Für die meisten Anwendungen Speed ist die richtige Wahl?Der Größenunterschied ist in der Regel nur 2-3,MB,,, aber die Leistungsdifferenz kann spürbar sein.

RuntimeIdentifiers

Dies erklärt, welche Plattformen Sie unterstützen möchten...Es doesns.'.t baut sie alle.MSC2.it sagt nur Tooling .MSSK3. Diese sind gültige Ziele.

Verfügbare Identifikatoren

  • win-x64Weitere Informationen finden Sie unter: http://www.cordis.lu/
  • win-arm64Windows ARM-64, (Surface Pro X.,, usw. .)
  • linux-x64Weitere Informationen sind abrufbar unter: http:www.cordis.lu/index.htm
  • linux-arm64Linux ARM-64-(-Raspberry Pi AWS Graviton
  • osx-x64MacOS Intel Macs mit älteren Macs
  • osx-arm64M:-MacOS-Apfelsilizium MMSC1-M.1/-M3-M3-Mcs-)-

Sie bauen eine Plattform zu einer Zeit mit dotnet publish -r linux-x64Z.B.:

Gebäude für mehrere Plattformen

Denken Sie daran, wie ich sagte AOT erfordert platform-.--spezifische Builds, ?-Sie müssen separat für Windows kompilieren ?,-Linux x-64,-Linux-AR64,-macOS Intel-M,-und macOS Apple-Silicium?.-Dies manuell zu tun wäre mühsam, aber wir können es automatisieren

Was ist GitHub Aktionen?

Wenn du nicht vertraut bist, ist die GitHub-Aktionen ein kostenloser CIP-Dienst./CD Kontinuierliche Integration.../"Continuous Deployment )-Dienst, der in GitHub" gebaut wurde, ermöglicht es dir, automatisierte Aufgaben auszuführen, wenn du Code drückst oder ein Release-Tag erstellst.

Denken Sie daran, wie mit einem Build-Server, dass

  1. Uhren Ihr GitHub Repository
  2. Wenn du ein Tag wie v1.0.0
  3. Es dreht sich automatisch nach oben Windows-,-Linux- und macOS-virtuellen Maschinen
  4. Baut Ihre App für alle Plattformen parallel
  5. Erstellt ein GitHub Release mit allen beigefügten Binärdateien

All dies läuft auf GitHub-''s Servern.—'you don.''t Notwendigkeit, irgendeine Infrastruktur zu pflegen..' Für offene -`source Projekte und kleine persönliche Projekte...,'s völlig frei

Der komplette Build-Workflow

Hier ist ein GitHub Actions Workflow, der für alle gängigen Plattformen automatisch erstellt wird.

name: Build Native AOT Binaries

on:
  push:
    tags:
      - 'v*'

jobs:
  build-binaries:
    name: Build ${{ matrix.runtime }}
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        include:
          - os: ubuntu-latest
            runtime: linux-x64
            artifact-name: myapp-linux-x64

          - os: ubuntu-latest
            runtime: linux-arm64
            artifact-name: myapp-linux-arm64

          - os: windows-latest
            runtime: win-x64
            artifact-name: myapp-win-x64

          - os: macos-latest
            runtime: osx-x64
            artifact-name: myapp-osx-x64

          - os: macos-latest
            runtime: osx-arm64
            artifact-name: myapp-osx-arm64

    steps:
    - uses: actions/checkout@v4

    - name: Setup .NET
      uses: actions/setup-dotnet@v4
      with:
        dotnet-version: '9.0.x'

    - name: Install ARM64 tools (Linux ARM64 only)
      if: matrix.runtime == 'linux-arm64'
      run: |
        sudo apt-get update
        sudo apt-get install -y clang zlib1g-dev gcc-aarch64-linux-gnu

    - name: Publish
      shell: bash
      run: |
        # Set objcopy for ARM64 cross-compilation
        if [ "${{ matrix.runtime }}" = "linux-arm64" ]; then
          OBJCOPY_PARAM="-p:ObjCopyName=aarch64-linux-gnu-objcopy"
        else
          OBJCOPY_PARAM=""
        fi

        dotnet publish \
          -c Release \
          -r ${{ matrix.runtime }} \
          --self-contained \
          --output ./publish/${{ matrix.runtime }} \
          -p:PublishAot=true \
          -p:PublishTrimmed=true \
          -p:StripSymbols=true \
          $OBJCOPY_PARAM

    - name: Create distribution package
      shell: bash
      run: |
        mkdir -p ./dist
        cd ./publish/${{ matrix.runtime }}

        # Copy the main executable
        cp myapp${{ matrix.file-ext }} ../../dist/

        # CRITICAL: Copy native libraries (SQLite and any other native dependencies)
        # PublishSingleFile bundles .NET code, but native DLLs remain separate
        cp *.dll ../../dist/ 2>/dev/null || true
        cp *.so ../../dist/ 2>/dev/null || true
        cp *.dylib ../../dist/ 2>/dev/null || true

        # Copy config files if needed
        cp ../../appsettings.json ../../dist/ || true

        cd ../../dist

        # Create archive with all files
        if [ "${{ runner.os }}" = "Windows" ]; then
          7z a -tzip ../${{ matrix.artifact-name }}.zip *
        else
          tar czf ../${{ matrix.artifact-name }}.tar.gz *
        fi

    - name: Upload artifact
      uses: actions/upload-artifact@v4
      with:
        name: ${{ matrix.artifact-name }}
        path: |
          ${{ matrix.artifact-name }}.zip
          ${{ matrix.artifact-name }}.tar.gz
        retention-days: 7
        if-no-files-found: ignore

Wie dieser Workflow funktioniert Schritt für Schritt

Wenn das YAML sieht einschüchternd aus hier was es im Klaren Englisch macht

Auslösung des Auslöserson: push: tags)

Der Workflow läuft, wenn Sie ein Git-Tag drücken, das mit v ,( , ähnlich v1.0.0, v2.3.1Dies ist die Standard-Methode, um Release-Versionen zu markieren.

Matrix-Strategie

Dies ist der clevere Teil, statt fünf separate Workflows zu schreiben, definieren wir eine Matrix von Builds-:

  • Jeder Bau bekommt einen anderen os Maschinen, Apparate und Geräte runtime Zielplattform)
  • GitHub Actions läuft alle fünf Builds parallel
  • Jeder produziert ein Artefakt, das kompilierte Binärmaterial.

Wenn du also drückst v1.0.0GITHUB simultan :

  • Dreht sich eine Ubuntu-Maschine, um Linux zu bauen x- und ARM-64
  • Dreht sich eine Windows-Maschine, um Windows zu bauen
  • Dreht eine macOS-Maschine hoch, um mac OS x- und ARM-64 zu bauen

Schritte in jedem Bau

Jede Plattform baut die gleichen Schritte

  1. KassencodeBekommt Ihren Quellcode aus dem Repository
  2. Konfigurieren von .-NETInstallation von . NET SDK
  3. ARM-64-Werkzeuge installieren Linux ARM-64 Installiert Werkzeuge für die cross-Sammlung von -
  4. VeröffentlichenLaufende Nummer dotnet publish mit AOT-Flags für die jeweilige Plattform
  5. Artefakt hochladenSpeichert die kompilierte Binärdatei, damit der nächste Auftrag darauf zugreifen kann

Das Ergebnis

Nachdem alle Builds komplett sind, haben Sie fünf Artefakte.(.binaries.). bereit zum Verteilen von .. Sie können sie aus den Aktionen herunterladen.MSC4. oder einen zweiten Job verwenden, um ein GitHub Release automatisch zu erstellen.

Kritisch ,: , Native Bibliotheken sind nicht gebündelt

Hier ist etwas, das mich für Stunden verwirrt hat. PublishSingleFile=true Nur Bundles-Code für .-NETNative Bibliotheken wie SQLite, ' e_sqlite3.dll bleiben Sie getrennt.

Aus diesem Grund ist der Schritt "Verteilungspaket erstellen" so wichtig.

# Copy native libraries - these are NOT included in the main executable
cp *.dll ../../dist/ 2>/dev/null || true    # Windows
cp *.so ../../dist/ 2>/dev/null || true     # Linux
cp *.dylib ../../dist/ 2>/dev/null || true  # macOS

Das 2>/dev/null || true Teil bedeutet ", wenn es keine Dateien gibt, die mit diesem Muster übereinstimmen.,, dont. ',t fail."—,just continue".", Dies lässt dasselbe Skript auf allen Plattformen funktionieren.

Was du austeilst

  • Fenster mit einem Durchmesser von mehr als 2 mm myapp.exe + e_sqlite3.dll zusammen in einem ZIP-)
  • Linux®:- myapp + libe_sqlite3.so zusammengeschweißt in einem tar.., gz, ),
  • macOS-: myapp + libe_sqlite3.dylib zusammengeschweißt in einem tar.., gz, ),

Benutzer extrahieren das Archiv und führen die ausführbare Version aus.Die native SQLite-Bibliothek sitzt neben der ausführbaren Datei und das Bundle findet es automatisch zur Laufzeit.

Warum manuelles Triggern nützlich ist

Beachten Sie die workflow_dispatch Trigger an der Oberseite

on:
  push:
    tags:
      - 'v*'
  workflow_dispatch:
    inputs:
      version:
        description: 'Version to publish'
        required: true

So können Sie Builds manuell von GitHub-'aus der Weboberfläche auslösen, ohne einen Tag zu drücken..e Nützlich zum Testen des Workflows oder zur Erstellung von Beta-Builds.

ARM-64-Cross---Compilation

Der Linux-ARM-64-Build erfordert besondere Aufmerksamkeit. Sie benötigen cross-MSC2-Compilation-Tools und müssen die richtigen objcopy Werkzeug„:"

# Install tools
sudo apt-get install gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu

# Build with objcopy specified
dotnet publish \
  -r linux-arm64 \
  -p:PublishAot=true \
  -p:ObjCopyName=aarch64-linux-gnu-objcopy

Ohne die ObjCopyName Parameter: Der Linker schlägt mit kryptischen Fehlern über unerkannte Dateiformate fehl

Ergebnisse der Welt

Hier ist, was ich mit meiner Produktion YARP--.based Bot Detection Gateway mit Middleware und SQLite Logging erreicht habe. Download von GitHub.

Binäre Größen aus tatsächlichen GitHub-Veröffentlichungen

Das sind die tatsächliche Dateigrößen aus meinem GitHub veröffentlicht nicht theoretische Schätzungen

Ausführbare SQLite Native DLL Archivgröße |----------|-----------|-------------------|-------------------| Weitere Informationen finden Sie unter: http:www.cordis.lu/ 10.9-MB ](-ZIP-)-Zip-|- Weitere Informationen: http:www.cordis.lu/index.html 12.4-MB (-tar-.-gz®)-g Linux ARM-64, |, M9.9MB, MKSK4, +1.5, MCSK6 11.4-MB (-tar-.-gz®)-g Weitere Informationen sind abrufbar unter: http:www.cordis.lu/ 13.0-MB (-tar-.-gz®)-g M|-MacOS-AR64-|-9.8-MB-MSC4-MSSK5-MB 11.5-MB (-tar-.-gz®)-g

Vergleichen Sie dies mit den in sich geschlossenen Bereitstellungen von ..NET. 130-150,MB pro PlattformWir sprechen über eine Verkleinerung der Größe.

Der Zusammenbruch

  • Wichtigste ausführbare Version Ihr kompilierter Code +...NET Laufzeit (.-.compiliert ).
  • Die SQLite Datenbank-Engine von Native DLL.

Beide Dateien müssen zusammen verteilt werden, aber sie sind immer noch dramatisch kleiner als die herkömmlichen . NET-Bereitstellungen.

Inbetriebnahmeleistung

Kalter Start Erste Anfrage auf einem bescheidenen Linux-VPS-:

  • Selbstbeherrschendes --integriertes .-NET,:, , ~800,ms
  • Natives AOT,:, , ~150,ms
  • VerbesserungSchneller

Dies ist sehr wichtig für die

  • Serverlos ,/ ,Lambda-Funktionen , ( , zahlen Sie pro Millisekunde , ) ,
  • Container, die häufig nach oben skalieren
  • CLI-Werkzeuge, bei denen jede Anrufung neu beginnt

Speichernutzung

Idle-Speicher (-Gateway läuft ,-kein Verkehr

  • Selbstbeherrschendes --integriertes .-NET:, 85MB
  • Natives AOT:, 42MB
  • VerbesserungVerringerung der CO2-Emissionen

Unter Last Anforderungen /.Sekunden):.

  • Selbstbeherrschendes --integriertes .-NET:, ~320MB
  • Natives AOT:, ~180MB
  • VerbesserungVerringerung der CO2-Emissionen

Unterer Speicher bedeutet:„:"

  • Mehr Container pro Wirt
  • Billigere Cloud-Hosting Rechnungen
  • Machbarkeit auf ressource---beschränkten Geräten

Häufige Pitfalls

Vergeudung Batteries.Init()

Dies ist der Fehler von ., selbst mit dem korrekt referenzierten Bundle, vergessen zu rufen SQLitePCL.Batteries.Init() am Anfang Ihrer Main Methode verursacht Laufzeitabstürze

Die Fixierung

public static void Main(string[] args)
{
    SQLitePCL.Batteries.Init(); // FIRST LINE
    // ... rest of your code
}

Native Bibliotheken nicht verteilen

Dies ist ein Fehler, und es hat mich zweimal bekommen. PublishSingleFile=true does NOT bündeln native SQLite DLLs in Ihre ausführbare Version. Sie müssen sie neben Ihrer ausführbaren Datei verteilen.

Was passiert, wenn Sie vergessen?

  • Deine App baut gut auf
  • Es läuft in Ordnung auf Ihrem dev-Rechner, wo SQLite bereits installiert sein könnte.
  • Es stürzt in der Produktion mit DllNotFoundException

Die Fixierung

Beim Verpacken Ihres Releases sind , immer inbegriffen.

myapp.exe               # Your main executable
e_sqlite3.dll           # SQLite native library (Windows)
# or libe_sqlite3.so    # SQLite native library (Linux)
# or libe_sqlite3.dylib # SQLite native library (macOS)

In Ihren GitHub-Aktionen oder Bereitstellungsskripten

# Copy ALL native libraries from the publish directory
cp *.dll ./dist/ 2>/dev/null || true
cp *.so ./dist/ 2>/dev/null || true
cp *.dylib ./dist/ 2>/dev/null || true

Benutzer sollten das Archiv extrahieren und die ausführbare Datei ausführen.Die native Bibliothek muss im gleichen Verzeichnis sein.

Verwendung winsqlite3 Anbieter

Vielleicht sehen Sie Empfehlungen zu verwenden SQLitePCLRaw.provider.winsqlite3 unter Windows, um das OS-- zur Verfügung gestellt SQLite .. . Don ?' . . . Es funktioniert nur auf Windows erfordert manuelle Initialisierung und bricht cross -- . bundle_e_sqlite3.

Warnungen mit EF-Kern

Wenn Sie den Entity Framework Core mit SQLite verwenden, erhalten Sie die Trimmwarnungen von '. Diese sind normalerweise sicher für EF-Core zu unterdrücken.

<PropertyGroup>
  <NoWarn>$(NoWarn);IL2026;IL3050</NoWarn>
</PropertyGroup>

Besser noch: ,, in Betracht ziehen, Dapper oder Roh ADO-.,NET mit SQLite für AOT-Anwendungen zu verwenden. —,they-',re mehr: AOT.-,friendly

Fehlende visuelle Studio-Werkzeuge

Unter Windows erfordert Native AOT Visual Studio, ' und MSVC-Linker, wenn Sie außerhalb eines Developer Command Prompt, bauen, erhalten Sie Fehler über vswhere.exeGitHub Aktionen behandelt diese automatisch, aber für lokale Builds.

  • Entwickler-Befehlsaufforderung für VS
  • Entwickler PowerShell für VS

Oder initialisieren Sie die Umgebung in Ihrem Build-Skript

& "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\Launch-VsDevShell.ps1" -Arch amd64
dotnet publish -c Release -r win-x64

Wann Native AOT verwendet werden soll

Native AOT ist perfekt für die

  • CLI-WerkzeugeSofortige Inbetriebnahme ist wichtig
  • Mikroservices und ContainerKleinere Bilder Schnellere Skalierung
  • KanteneinrichtungenIoT-Geräte mit begrenzten Ressourcen
  • Serverlos, /-LambdaKalte Startzeit ist entscheidend
  • Gateway-Anwendungen für /ProxyHohe Durchsatzleistung Niedriger Overhead

Vermeiden Sie AOT für

  • Schweres Entity-Framework Core-Verwendung Viel Migrationen – komplexe Abfragen – )
  • Reflection---schwere Anwendungen
  • Dynamische Plugin-Systeme
  • Anwendungen, die Laufzeitcode-Generierung benötigen

Schnellstart-Checkliste

Wenn du diesen ganzen Beitrag liest und nur eine Checkliste willst, um hier zu folgen

Die Pakete hinzufügen

<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.10" />

Konfigurieren Sie Ihre .csproj für AOT-:-

<PublishAot>true</PublishAot>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>full</TrimMode>
<InvariantGlobalization>true</InvariantGlobalization>
<PublishSingleFile>true</PublishSingleFile>
<StripSymbols>true</StripSymbols>
<OptimizationPreference>Speed</OptimizationPreference>

Starten Sie SQLite am Anfang Ihrer Main Methode:

SQLitePCL.Batteries.Init();

Bauen Sie für Ihre Zielplattform

dotnet publish -c Release -r linux-x64 --self-contained

Testen Sie Ihre binäre Version sollte nur ohne Abhängigkeiten laufen.

Schlußfolgerung

SQLite zu bekommen, die mit Native AOT arbeitet, ist nicht offensichtlich, aber sobald Sie die magische Beschwörung kennen.SQLitePCLRaw.bundle_e_sqlite3 + Batteries.Init()Die Auszahlung ist substantiell , die winzigen binaries , , , Instant-Startup ,, , und die Fähigkeit, auf jeder Plattform ohne Laufzeitabhängigkeiten bereitstellen ,

Für jeden, der CLI-Tools baut, sind die Gateways von , oder Edge-Anwendungen mit SQLite nun eine realistische Option Der hier bereitgestellte GitHub Actions Workflow automatisiert den gesamten Multi-- platform build Prozess.

Wenn du ein einfaches CLI-Tool oder Dienstprogramm zuerst umwandelst Bequem mit dem Build-Prozess lernen Sie, welche Warnungen zu erwarten sind , und verstehen Sie die Einschränkungen messk6 Sobald Sie die Basics unten haben down mess k8 können Sie komplexere Anwendungen angehen

Die meisten modernen Bibliotheken funktionieren entweder aus dem Kasten heraus oder haben eine klare Dokumentation auf AOT-Unterstützung.Die Zukunft ist natives... und es ist schneller, als Sie denken.

Reale Beschäftigungserfahrung

Ich entwickle mein AOT---kompiliertes Gateway zu ,:'

  • Digital Ocean Droplet Linux x-64), läuft als Systemdienst
  • Himbeer-Pi Linux-ARM-64), Kantenausbringung zum Testen
  • Windows Server Windows x.64). läuft als Windows-Dienst
  • Dockerbehältnisse Die beiden Varianten x,64 und ARM, 64

Der Deployment-Prozess ist überall identisch

  1. Das Archiv von GitHub Releases herunterladen
  2. Es extrahieren
  3. Ausführen der ausführbaren Datei

Keine Installation von . NET Laufzeit" schritt. , keine Abhängigkeit hell. , Keine Versionskonflikte , nur extrahieren und ausführen

Für Docker-, Dockerfile ist peinlich einfach

FROM debian:bookworm-slim

# Copy just the two files we need
COPY minigw /app/minigw
COPY libe_sqlite3.so /app/libe_sqlite3.so

WORKDIR /app
RUN chmod +x minigw

EXPOSE 5000
ENTRYPOINT ["./minigw"]

Das resultierende Bild ist ~130-MB Die meisten Debian-Images aus der Basis). Ein traditioneller Container von .'NET wäre 200-250.MB...

Vollständiges Arbeitsbeispiel

Alles, was hier gezeigt wird, kommt aus einer echten ?,?-Produktion?MSC2?ready project?.?-Sie können ♪:?-

Das Projekt ist ein minimaler YARP Reverse Proxy mit Bot Detection Middleware, der Signaturen auf SQLite protokolliert.

  • Native AOT mit SQLite
  • Multi---Plattform baut über GitHub Aktionen
  • Einheimische Bibliothek bündeln
  • CLI-Argument zur Parsierung
  • Strukturierte Protokollierung
  • Anmutige Abschaltung

Es als Vorlage für Ihre eigenen AOT-Projekte verwenden

Aufkommen

Jetzt gehen Sie etwas winziges und schnelles bauen

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.