Multiplatform AOT mit SQLite-:, wie man es funktioniert
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
- Ihr Computer lädt das separate Programm (
- Die Laufzeit liest Ihren IL-Code
- Nur in der Zeit von (JIT-) compiler konvertiert IL in nativen Maschinencode, während Ihre App läuft
- 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.EmitArten 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.
- Sie fügen hinzu:
Microsoft.Data.Sqlitezu Ihrem Projekt - Sie konfigurieren
PublishAot=truein Ihrem.csproj - Das Build komplettiert ohne Fehler alles sieht gut aus.
- 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.dllunter Windowslibe_sqlite3.sounter Linuxlibe_sqlite3.dylibauf 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
- Die Laufzeit kann Bibliotheken dynamisch laden
- NuGet-Pakete können native Bibliotheken für alle Plattformen enthalten
- 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.
- Finden Sie die richtige native SQLite-Bibliothek für Ihre aktuelle Plattform
- Laden Sie es in den Speicher
- Verkabeln Sie alle Verbindungen zwischen
Microsoft.Data.Sqliteund 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 :
- Mit Erfolg Builden Sie den Compiler Won , ' , warnt Sie nicht
- Starten Sie
- 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=trueermöglicht das BeschneidenTrimMode=fullbedeutet, 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 LeistungSizeMachen 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.htmlinux-arm64Linux ARM-64-(-Raspberry Pi AWS Gravitonosx-x64MacOS Intel Macs mit älteren Macsosx-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
- Uhren Ihr GitHub Repository
- Wenn du ein Tag wie
v1.0.0 - Es dreht sich automatisch nach oben Windows-,-Linux- und macOS-virtuellen Maschinen
- Baut Ihre App für alle Plattformen parallel
- 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
osMaschinen, Apparate und GeräteruntimeZielplattform) - 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
- KassencodeBekommt Ihren Quellcode aus dem Repository
- Konfigurieren von .-NETInstallation von . NET SDK
- ARM-64-Werkzeuge installieren Linux ARM-64 Installiert Werkzeuge für die cross-Sammlung von -
- VeröffentlichenLaufende Nummer
dotnet publishmit AOT-Flags für die jeweilige Plattform - 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.dllzusammen in einem ZIP-) - Linux®:-
myapp+libe_sqlite3.sozusammengeschweißt in einem tar.., gz, ), - macOS-:
myapp+libe_sqlite3.dylibzusammengeschweiß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
- Das Archiv von GitHub Releases herunterladen
- Es extrahieren
- 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 ♪:?-
- Siehe den Quellcode: Meistlylucid, ., BotDetection,., Konsole
- Siehe den Workflow von GitHub-Aktionen: Konsole--Gateway--Release.-yml
- Vorgefertigte Binärdateien herunterladen: GitHub veröffentlicht
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
- Offizielle Dokumentation der Native AOT-Dokumentation
- SQLitePCLRaw GitHub Repository
- Arbeitsbeispiel: :-Mostlylucid Bot Detection Gateway
- Mein GitHub-Aktionen Workflow
Jetzt gehen Sie etwas winziges und schnelles bauen