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
Multiplatform AOT avec SQLite: Comment le faire fonctionner
Tuesday, 16 December 2025
Introduction
L’AOT natif promet de transformer vos applications .NET en minuscules , autos - contenant des executables qui commencent instantanément et fonctionnent partout M SK3 aucune installation d’exécution n’est requise DllNotFoundException crashes, combattant avec des dépendances transitoires, et se demandant pourquoi PublishSingleFile=true doesn't actually bundle everything into a single file . This is the complete, beginnerMSC3toM SK4expert guide on getting SQLite working with Native AOTMska5 building for multiple platformsM Ska6 and automating releases with GitHub Actions M Ska7 By the endM ska8 youMske9ll have MSKA10MB binaries that run on WindowsMská11 Linux Mska12including Raspberry PiMsaka13 and macOSMaskan14with zero dependenciesMzaka15
Qu’est-ce que l’OT?
Let's commencer par les bases absoluesM SK1 Si vous' avez déjà construit une application .NET et que vous vous demandez pourquoi vous devez installer l’application \ ".NET Runtime " sur des serveurs ou pourquoi votre application de console prend un second pour commencer la première fois qu’elle est exécutée, le logiciel AOT est la réponse à ces problèmes.
Comment .NET fonctionne normalement (Compilation JIT)
Lorsque vous écrivez un code C# et que vous construisez votre application, le compiler ne produit pas de code machine que votre CPU peut exécuter directement. Langue intermédiaire (IL)—considérez-la comme la moitié de votre cheminement entre le code C # et les instructions exactes de la machine.
Lorsque vous exécutez votre .NET app, ici 's ce qui se passeM SK3
- Votre ordinateur charge le .NET Runtime (un programme distinct)
- Le temps d'exécution lit votre code IL
- Just-In-Time (JITM SK3 compiler convert IL en code natif de machine à mesure que votre application fonctionne
- Votre CPU exécute enfin ce code de la machine
C’est comme avoir un traducteur qui lit votre recette (ILM SK1 et la traduit verbalement à un chef (CPU) ligne par ligne alors qu’ilsMSC4re cuissonsMST5 Il fonctionneMSP6 mais il y aMSS7 surcoûtMSSS8
- Le temps d'exécution .NET est de 50-150MB de fichiers supplémentaires que vous devez déployer
- Le compilateur JIT prend du temps pour traduire le code (comment votre app est lente la première fois)
- Le compilateur JIT lui-même se trouve dans la mémoire pendant que votre application fonctionne
Inscrire AOT (Ahead -OfM SK2Time Compilation)
AOT autochtone tourne ce modèle sur sa tête. Au lieu de traduire votre code en temps d’exécution, il traduit tout au moment de la construction. Vous obtiendrez un seul fichier exécutable qui contient le code réel de votre ordinateur et que votre CPU peut exploiter directement.
Pensez à ce qu’il s’agisse d’obtenir un livre de recettes traduit par des professionnels plutôt que d’embaucher un traducteur en direct.
Le jeu-Change des avantages
Voici ce que vous donne l’AOT autochtone
1. Moins exécutoires: 10-30MB au lieu de 150MBM SK3
Votre app et tout ce dont elle a besoin est compilé en un petit binaire. Aucun fichier de temps d'exécution distinct.
2. Démarrage immédiat: 80% début de froid plus rapide
Mes essais: normal M SK1NET a pris ~800ms pour commencer, AOT a taken ~150msMST5 Il n’y a pas de JIT warmMSC7temps d’allumageMNK8le code de la machine est prêt à être exécuté immédiatementMRK9
3. Nulle dépendance: Non .Durée de fonctionnement du réseau requise
Vous pouvez copier votre executable à n'importe quelle machine avec le bon OS (Windows/LinuxM SK2MacMSC3 et il fonctionne tout simplement . Aucune "installer | | .NET |
4. Utilisation réduite de la mémoire: À propos de 50% moins mémoire
Aucun compilateur JIT ne se trouve dans la mémoire. Dans ma application de passerelle, normal M SK2NET utilisé 85MB non fonctionnelMSC4 AOT utilisé
5. Amélioré pour les milieux restreints: Travaux où le JIT peut
Certains environnements (certains conteneurs de Docker, iOSM SK2 systèmes intégrés ) ne permettent pas la génération du code de temps d'exécution
Le commerce-offs M SK1There's Always a Catch )
L’OT n’est pas une magie, mais vous êtes une flexibilité de temps d’exécution pour l’optimisation avant-première.
1. Aucune génération de code dynamique
Tout ce qui génère des codes à l'heure d'exécution win't work:
System.Reflection.Emit(création de types dynamiquesM SK1- Chargement de l'assemblage dynamique (chargement des DLL au moment du démarrage)
- Quelques trucs de réflexion fancieux
La plupart des codes généraux .NET sont bons, mais certains cadres qui dépendent fortement de la réflexion ont besoin d’une configuration spécialeM SK2
2. Platform-Builds spécifiques
La compilation JIT produit un IL qui fonctionne sur n’importe quelle plate-forme. AOT produit le code natif de la machine pour une plate-forme spécifique. Vous devez construire séparément pour
- Windows x64
- Linux x64
- Linux ARM64 (Raspberry Pi)
- macOS Intel
- macOS Apple Silicon
Nous ' nous aborderons de l’automatisation avec les actions de GitHub plus tard .
3. Durée de construction plus longue
Plutôt que de compiler à l’IL en secondes, AOT compile tout le chemin jusqu’au code de la machine. Atteindre M SK2 minutes au lieu de 10 secondes
4. Certaines fonctionnalités nécessitent une configuration supplémentaire
JSON serialisation, Cadre d'entités, et tout ce qui fait appel à la réflexion lourde peut nécessiter que vous dites explicitement au compiler quels types de fichiers il doit conserver.
Quand utiliser AOT?
AOT autochtone est parfait pour:
- Outils CLI: Commande-utilités de ligne où le démarrage immédiat est important
- Microservices: Images de docker plus petites, escalation plus rapide dans Kubernetes
- Serverless/Lambda: Le temps de début froid a une incidence directe sur votre facture
- Dispositifs de bord: Raspberry Pi, Dispositifs IoT à ressources limitées
- Applications de la passerelle: Proxies inverses, passerelles API à haut débit
- Outils de bureau: Envoyez un seul .exe sans "installer M.NET premier" étape
Passer AOT pour:
- Applications Web traditionnelles où le temps de démarrage n’est pas important
- Applications utilisant un cadre d'entités lourd avec de nombreuses migrations
- Systèmes de mise en place qui chargent les DLL dynamiquement
- Tout ce qui génère un code au moment du démarrage
Pour les outils CLI, microservices , conteneurs, et dispositifs de bordM SK3 les améliorations sont le jeuMSC4changingMST5 Mais il y aMSL6un piège quand vous ajoutez des bases de donnéesMSV7spécifiquement SQLiteM SV8
Le problème SQLite
Maintenant que vous comprenez ce qu’est un AOT, laissez ' parler du point de souffrance le plus important.
Ce qui se passe (L'expérience frustrante)
Voici le parcours typique de '
- Vous ajoutez
Microsoft.Data.Sqliteà votre projet - Vous configurez
PublishAot=truedans votre.csproj - La construction se termine sans erreurs—tout semble bon!
- Vous démarrez l’exécutoire et obtiendrez immédiatement:
DllNotFoundException: Unable to load DLL 'e_sqlite3' or one of its dependencies
Votre application se démarque avant de pouvoir faire quoi que ce soit. Ce qui donne?
Comprendre les bibliothèques autochtones (A Quick Detour)
Pour comprendre le problème, vous devez savoir Bibliothèques autochtones.
SQLite n’est pas 't écrite dans .NET—itM SK3s écrite en CMSC4 Il est 's compilé à la plate-formeMNK6code natif spécifiqueMRK7
e_sqlite3.dllsur Windowslibe_sqlite3.sosur Linuxlibe_sqlite3.dylibsur macOS
Lorsque vous utilisez Microsoft.Data.Sqlite dans la normalité .NET, ilM SK2 est simplement un enveloppe autour de cette bibliothèque SQLite native . À l’heure du démarrageMSC4 il essaie charge dynamiquement le fichier natif approprié pour votre plate-forme.
Cela fonctionne bien avec le .NET normal parce que
- Le temps d'exécution peut charger les bibliothèques dynamiquement
- Les paquets NuGet peuvent inclure des bibliothèques natives pour toutes les plates-formes
- La droite est sélectionnée au moment du démarrage.
Pourquoi l’OT interrompt-il cela?
L’OT autochtone a deux caractéristiques qui s’opposent à l’approche SQLite'
1. Traitement agressif: AOT supprime tout code qu'il croit que vous utilisez ' Vous ne l'utilisez pas. Si elle peutM SK3t prouver statiquement que vous avez besoin de quelque choseMSC4 elle est suppriméeMST5 La chargement dynamique de la bibliothèque confond le trimmer MST6elle ne peutSST7voir la connexion entre votre code et le DLL natif SQLiteMst8
2. Aucun support de chargement dynamique: L'AOT produit un binaire contrôlé par lui-même - Il s'attend à ce que toutes les dépendances indigènes soient explicitement liées au moment de la compilation
Le résultat: Microsoft.Data.Sqlite attend de trouver une bibliothèque SQLite native au moment du démarrage, mais AOT l’a soit enlevée, soit ne sait pas comment la regrouper correctement
Le noyau de la dépendance transitoire
Même pire, si vous avez d’autres paquets NuGet qui utilisent SQLite (comme certaines bibliothèques ORM ou mon mostlylucid.ephemeral.complete package), vous pouvez finir par plusieurs fournisseurs SQLite incompatibles dans votre arbre de dépendance.
Chaque fournisseur essaie de travailler différemment:
- On peut s’attendre à ce que l’OS-provide SQLite (Windows seulementM SK2
- Un autre pourrait regrouper ses propres SQLite
- Un autre pourrait utiliser une version de bibliothèque indigène différente.
Le compilateur AOT se méconnaît quant à l’utilisation de ce dernier, et le résultat est souvent que celui-ci ne comprend aucun d’entre eux.
La solution: Utiliser le Bundle
La solution est SQLitePCLRaw.bundle_e_sqlite3—un paquet NuGet spécial conçu spécifiquement pour travailler avec AOT
Ajouter ces deux paquets au projet:
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.10" />
</ItemGroup>
Que fait le Bundle?
L'article 5 de la Loi sur l'immigration et le statut des réfugiés bundle_e_sqlite3 le paquet diffère des fournisseurs normalisés de SQLite:
1. Il comprend des bibliothèques natives SQLite pré-compilées pour chaque grande plateforme.
- Windows (xM SK1 x 86, ARM64)
- Linux (x 64, ARM64, muslM SK3distributions basées sur AlpineMSC4
- macOS (x 64 Intel, ARMM SK3 Apple SiliconMSC4
2. Ces bibliothèques natives sont emballées de la manière que le compilateur AOT comprend
Le paquet marque explicitement ses dépendances natives de sorte que le compiler AOT sait les inclure dans le binaire final. Aucun chargement dynamique, aucune recherche en temps d'exécution pour les fichiers .
3. Il ' est conçu pour les bâtiments de plate-forme croisés
Un paquet fonctionne pour toutes les plates-formes. Vous n’avez pas besoin de plate-forme'ne vous avez pas besoin d’un paquet spécifique ou de références conditionnelles .
Pourquoi cela fonctionne?
N'oubliez pas les deux problèmes que nous avons identifiés?
Problème 1: AOT 's trimming remove libraries it can't see being used
- Solution: Le paquet déclare explicitement ses bibliothèques natives comme des actifs de construction qui doivent être inclus
Problème 2: AOT ne supporte pas le chargement dynamique
- Solution: Le paquet relie statiquement les bibliothèques SQLite au moment de la compilation
Le résultat: lorsque vous construisez pour Windows x64, le paquet inclut e_sqlite3.dll. Lorsque vous construisez pour Linux ARM64, il inclut l’ARMM SK2 libe_sqlite3.so. Tout fonctionne tout simplement .
Critical: Initialiser le Bundle (DonM SK2t Passer à ce qui suit!)
Voici la partie qui attire les gens qui tentent d’utiliser SQLite avec AOT, dont moi lors de ma première tentative.
Avec .NET normal, le paquet SQLite s’initialise automatiquement la première fois que vous utilisez SQLite . La magie se produit derrière les scènes — vous n’avez pas besoin de faire quelque chose
Avec les AOT autochtones, cette initialisation automatique ne fonctionne pas 't workM SK2 Le compiler AOT peut't voir le code de démarrage automatique (il apparaît comme un code inutilisé et est trimé doit initialiser manuellement le paquet au début de votre application.
Voici la ligne magique dont vous avez besoin.
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();
}
}
Qu'est-ce qui fait Batteries.Init() Do?
Cette méthode indique le paquet SQLite à:
- Trouver la correcte bibliothèque native SQLite pour votre plateforme actuelle
- Chargez-le à la mémoire
- Connectez toutes les connexions entre
Microsoft.Data.Sqliteet la bibliothèque autochtone
Elle est appelée "Batteries" parce que c’est 'les batteries sont inclusesM SK5l’ensemble de —tout ce dont vous avez besoin est emballé ensemble .
Où le mettre
Placer Batteries.Init() En ce qui a trait à la protection des droits de l'homme et aux libertés fondamentales, la première ligne dans votre Main méthode, avantM SK1
- Création du constructeur d'application
- Configuration de l'injection de dépendance
- Ouverture de toute connexion à la base de données
- Lecture de fichiers de configuration qui pourraient utiliser SQLite
Pensez à cela comme s'il y avait eu craquage d'un périphérique avant de le mettre en marche. Si vous essayez d'utiliser SQLite avant d'appeler Init(), vous ' recevrez toujours le DllNotFoundException même si le DLL est correctement bundlé dans votre application.
Qu’arrive-t-il si vous oubliéez?
Si vous avez oublié de téléphoner Batteries.Init(), votre app sera
- Construire avec succès (le compiler a remporté 'ne vous avertit pas
- Commencer à fonctionner
- Perturber le moment où elle essaie d’utiliser SQLite avec:
DllNotFoundException: Unable to load DLL 'e_sqlite3' or one of its dependencies
Cela est confusant parce que le DLL est bundled in your application—itM SK1s just not initialized.
Configuration complète du projet
Voici une liste complète de MSK .csproj Configuré pour multi platform Native AOT avec 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>
Éclaircissement des paramètres clés (En anglais simples)
Permettez-moi de vous expliquer ce que chacune de ces configurations fait:
PublishAot=true
Il s'agit de l'interrupteur principal qui permet la compilation AOT native. Sans ce , vous obtiendrez le comportement normal .NET
PublishTrimmed=true et TrimMode=full
Ils disent au compilateur de supprimer tout code que vous n’utilisez pas.
PublishTrimmed=truepermet le triageTrimMode=fullsignifie "être agressifM SK1lever tout ce que vous pouvez"
Avertissement: Cela peut détruire le code qui fait appel à des réflexions lourdes (comme certains serialiseurs JSON ou ORMs M SK2 car le trimmer ne peut ' pas toujours voir ce que vous utilisez via les réflexions
InvariantGlobalization=true
Cela supprime toutes les données culturelles-données spécifiques de votre appM SK1formats de date,symboles monétairesMSC3règles de tri des textes pour différentes langues . Sauvegarde MNK5MBMRK6
Seules les valeurs suivantes sont indiquées : true si votre app:
- Utilise seulement l'anglais
- N'est-il pas nécessaire de formatter la date spécifique de -, de la culture ou du temps?
- Utilise seulement les comparaisons de chaînes ordinaires (byte-byM SK2byte≥)
Si vous ' construisez un outil CLI ou une passerelle API qui ne s’occupe pas de la localisation' , c’est une économie gratuiteM SK3 si vous ' construisons quelque chose qui doit formater les dates pour les utilisateurs français ou trier correctement le texte turcMSC5 reportez ce paramètreMST6
PublishSingleFile=true
Bundles tout en un seul fichier exécutoire. Au lieu d’avoir:
myapp.exe
myapp.dll
System.Text.Json.dll
... 50 more files
Vous obtiendrez juste:
myapp.exe
Plus facile à déployer.
StripSymbols=true
Les symboles de débugging aident les débugeurs à vous montrer des noms variables et des numéros de ligne lors du débuge. IlsM SK1 sont utiles pendant le développement mais ajoutent plusieurs mégabytes à votre binaire final.
Ce réglage les supprime. Votre app fonctionne exactement la même façonM SK1 simplement plus petite.
OptimizationPreference=Speed
Cela indique au compiler quelle priorité il doit accorder lorsqu’il prend des décisions:
Speed: Faire en sorte qu'il soit rapide (binoires légèrement plus grands, mais meilleure performanceM SK3Size: Faire en sorte qu'il soit petit
Pour la plupart des applications, Speed est le bon choix. La différence de taille n’est habituellement que 2-3MBM SK2 mais la différence de performance peut être noticeable.
RuntimeIdentifiers
Cela indique quelles plates-formes vous voulez soutenir. Il ne les construit pas tous'il n'y a pas d'outils générauxM SK2 il dit simplement à l'outillage "ce sont des cibles validesMSC4
Identificateurs disponibles:
win-x64: Windows 64-bit (IntelM SK3AMD)win-arm64: Windows ARMM SK1 (Surface Pro X, etcMSC4linux-x64: Linux 64-bit (Ubuntu, DebianM SK4 RHELMST5 etcMSC6linux-arm64: Linux ARM64 (Raspberry Pi 4/5, AWS GravitonM SK4osx-x64: macOS Intelosx-arm64: macOS Apple Silicon
Vous construisez une plateforme à la fois en utilisant dotnet publish -r linux-x64, par exempleM SK1
Établissement pour plusieurs plates-formes
Rappelez-vous que j’ai dit que l’OT requiert une plate-forme-constructions spécifiques ? Vous devez compiler séparément pour Windows, Linux xM SK3 Linux ARMMSC4 macOS IntelMST5 et macOS Apple SiliconMSV6 Faire cela manuellement serait acharnéMSP7mais nous pouvons le automatiserMSS8
Qu'est-ce que GitHub Actions?
Si vous 'non connaissez pas , GitHub Actions est un service gratuit de CI/CD (Intégration continue~/Déploiement continu~) intégré à Git HubM SK6 Il vous permet d'effectuer des tâches automatisées chaque fois que vous poussez le code ou que vous créez une balise de diffusionMSC7
Pensez à cela comme s’il y avait un serveur de construction qui:
- Surveille votre répertoire GitHub
- Lorsque vous poussez un étiquette comme
v1.0.0 - Il tourne automatiquement les machines virtuelles Windows, ,, Linux et macOS.
- Établissez votre app pour toutes les plateformes en parallèle
- Crée une version de GitHub avec tous les binaires attachés
Tous ces éléments fonctionnent sur GitHub.
Le processus de travail complet pour la construction
Voici un flux de travail GitHub Actions qui construit automatiquement pour toutes les grandes plateformes.
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
Comment fonctionne ce processus de travail (Étape par étape)
Si le YAML semble intimidant, c’est ce qu’il fait en anglais simple.
1. Trigger on: push: tags)
Le flux de travail se déroule lorsque vous poussez un tag Git qui commence par v (comme v1.0.0, v2.3.1). C'est la façon standard de marquer les versions de mise en marché.
2. Stratégie de matrice
Il s’agit de la partie intelligente. Au lieu d’écrire cinq flux de travail distincts, nous définissons un matrices de constructions:
- Chaque édifice reçoit une
os(machine à courirM SK1 etruntime(plate-forme cible) - GitHub Actions exploite tous les cinq édifices en parallèle
- Chacune produit un artefact (le binaire compilé)
Ainsi, lorsque vous poussez v1.0.0, GitHub simultanément:
- Utilisez une machine Ubuntu pour construire Linux x64 et ARM64
- Fait fonctionner une machine Windows pour construire Windows x64
- Utilisation d'une machine macOS pour construire des systèmes x64 et ARM64
3. Étapes dans chaque bâtiment
Chaque construction de plate-forme fait les mêmes étapes:
- Code d'achat: Obtient votre code source du répertoire
- Établissement .NET: Installe un SDK
- Installer les outils ARM64 (Linux ARMM SK1 seulement): Installe des outils de compilation croisésMSC3
- Publier: Runs
dotnet publishavec des drapeaux AOT pour cette plateforme spécifique - Ajouter un artefact: Sauve le binaire compilé de sorte que l'emploi suivant puisse y avoir accès
4. Résultat
Après toutes les constructions terminées, vous avez cinq artefacts M SK1binaries) prêts à être distribués . Vous pouvez les télécharger de l'action RunMSC4 ou utiliser un deuxième travail pour créer automatiquement une version de GitHub |
Critical: Les bibliothèques autochtones ne sont pas groupées
Voici une chose qui m’a confondu pendant des heures. PublishSingleFile=true seulement les paquets .NET code. Bibliothèques natives comme SQLite's e_sqlite3.dll demeurer séparés.
C'est pourquoi l'étape "Create distribution package" est si importante
# 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
L'article 5 de la Loi sur l'immigration et le statut des réfugiés 2>/dev/null || true part signifie " s'il n'y a pas de fichiers qui correspondent à ce modèle , ne faites pas de faillite' ne faille pasM SK3 continuez simplementMSC4 Cela permet d'utiliser le même script sur toutes les plates-formes
Ce que vous distribuez:
- Windows:
myapp.exe+e_sqlite3.dll(bundlé dans un ZIPM SK1 - Linux:
myapp+libe_sqlite3.so(combinés dans un goudron - macOS:
myapp+libe_sqlite3.dylib(combinés dans un goudron
Les utilisateurs extraisent l'archive et effectuent le programme exécutoire. La bibliothèque native SQLite se trouve à côté du programme executif,, et le paquet le trouve automatiquement au moment de la mise en œuvre.
Pourquoi le trigger manuelle est utile
Veuillez noter que workflow_dispatch déclencher en haut:
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: 'Version to publish'
required: true
Cela vous permet de déclencher les mises en place manuellement à partir de l'interface Web du GitHub' sans pousser un étiquette. Utilisable pour tester le flux de travail ou créer des mises à niveau bêtaM SK2
ARM64 Cross-Compilation
L’architecture Linux ARM64 requiert une attention particulière . Vous avez besoin d’outils de compilation croisés- et vous devez spécifier les corrects objcopy Outil:
# 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
Sans le ObjCopyName paramètre, le linéaire échoue avec des erreurs cryptiques concernant les formats de fichier non reconnus.
Résultats mondiaux
Voici ce que j’ai réalisé avec ma production YARP-Gateway de détection des bots basé sur le middleware et l’enregistrement SQLite. C’est un projet réel que vous pouvez réaliser téléchargement de GitHub.
Les tailles binaires (Des mises à jour réelles de GitHub)
Il s’agit des tailles réelles des fichiers de mes mises à jour sur GitHub, non des estimations théoriques:
| Platform | Exécutable | SQLite Native DLL SSK3 Total de la taille des archives S | ||
|---|---|---|---|---|
| Windows xM SK1 | 9.2MB | 10.9MB (ZIPM SK1 | ||
| Linux x 12.4MB (tarM SK1gz) | ||||
| ARM Linux64 | 9.9MB | 11.4MB (tarM SK1gz) | ||
| macOS Intel | MSC2MB MSC3 | 13.0MB (tarM SK1gz) | ||
| macOS ARM64 | 9.8MB | 11.5MB (tarM SK1gz) |
Comparez cela à des déploiements - comportant .NET 10 au 130-150MB par plate-forme. Nous ' parlons d’un 10-12x réduction de la taille.
La ventilation:
- Main executable: Votre code compilé + .NET runtime SSK3AOTMSC4compiledM SK5
- DLL natif: Le moteur de base de données SQLite ( écrit en C)
Les deux fichiers doivent être distribués ensemble, mais ils sont encore beaucoup plus petits que les déploiements généraux.
Démarrage de la performance
Démarrage froid (première requête servie) sur un VPS Linux modeste :
- Self-contained .NET: ~800ms
- AOT autochtone: ~150ms
- Amélioration: 81% plus rapide
Cela est extrêmement important pour :
- Serverless/Funktions Lambda (vous paiez par millisecondeM SK2
- Contenants qui augmentent ou diminuent fréquemment
- Outils CLI où chaque invocation commence de nouveau
Utilisation de la mémoire
Memory inactive (gateway running, pas de traficM SK2
- Self-contained .NET: 85MB
- AOT autochtone: 42MB
- Amélioration: 51% réduction
Sous charge (1000 demandes/secondeM SK2
- Self-contained .NET: ~320MB
- AOT autochtone: ~180MB
- Amélioration: 44% réduction
Moins mémoire signifie:
- Plus de conteneurs par hôte
- Bills d'hébergement de nuages plus abordables
- Efficacité sur les ressources-dispositifs restreints M SK1Raspberry Pi, IoT )
Les chutes communes
1. Forgetting Batteries.Init()
Il s’agit de l’erreur #1 SQLitePCL.Batteries.Init() au début de votre Main la méthode causera des défaillances de temps d'exécution.
La solution:
public static void Main(string[] args)
{
SQLitePCL.Batteries.Init(); // FIRST LINE
// ... rest of your code
}
2. Les bibliothèques autochtones ne sont pas distribuées
C'est une erreur #2, et j'ai obtenu deux fois . PublishSingleFile=true ne regroupe pas les DLL natifs SQLite dans votre executable. Vous devez les distribuer à côté de l'executable.
Que se passe-t-il si vous oublierez:
- Votre application fonctionne bien
- Il fonctionne bien sur votre machine dev (où SQLite pourrait déjà être installée)
- Il débouche sur la production avec
DllNotFoundException
La solution:
Lorsque vous emballez votre rejet, toujours inclure:
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)
Dans vos actions GitHub ou scripts de déploiement:
# 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
Les utilisateurs devraient extraire l'archive et exécuter l'executable. La bibliothèque native doit être dans le même répertoire.
3. Usage winsqlite3 Fournisseurs
Vous pourriez voir des recommandations à utiliser SQLitePCLRaw.provider.winsqlite3 sur Windows pour utiliser l’OS-provided SQLite . Don'tM SK3 Il ne fonctionne que sur WindowsMSC4 requiert une initialisation manuelleMST5 et interrompt les croisements bundle_e_sqlite3.
4. Avertissements de triomphe avec le noyau EF
Si vous utilisez l’Entity Framework Core avec SQLite,, vous recevrez des avertissements de triage MSC3ILM SK4 IL3050). Ces avertisseurs sont habituellement sûres pour supprimer dans le cadre d’EF Core ' les fournisseurs SQLitesMNK7 mais ils doivent être vérifiés minutieusementMRK8
<PropertyGroup>
<NoWarn>$(NoWarn);IL2026;IL3050</NoWarn>
</PropertyGroup>
Par ailleurs, il est préférable d’utiliser le Dapper ou le ADO brut.
5. Outils d'atelier visuel manquants
Sur Windows, Native AOT requiert Visual Studio's MSVC linkerM SK2 Si vous construisez à l’extérieur d’un Command Prompt de développeur ,, vous effectuez des erreurs sur vswhere.exe. Les actions de GitHub le gèrent automatiquement, mais pour les édifices locaux
- Demande de commande du développeur pour VS 2022
- Developer PowerShell pour VS 2022
Ou initialiser l'environnement dans votre script de construction:
& "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\Launch-VsDevShell.ps1" -Arch amd64
dotnet publish -c Release -r win-x64
Quand utiliser l'AOT autochtone
AOT autochtone est parfait pour:
- Outils CLI: Questions de démarrage immédiat
- Microservices et conteneurs: Images plus petites , Échelle plus rapide
- Dispositifs de bord: Raspberry Pi, Dispositifs IoT à ressources limitées
- Serverless/Lambda: Le temps de départ froid est crucial
- Applications Gateway/proxy: haut débit , faible surcoût
Avoid AOT for:
- Cadre d’entités lourdes Usage du noyau (manque de migrations,questions complexes )
- Réfléction-applications lourdes
- Systèmes de plugin dynamique
- Applications qui ont besoin d'une génération de code en temps d'exécution
Liste de vérification du début rapide
Si vous' avez lu tout ce post et que vous voulez simplement une liste de vérification à suivre , ici vous allez:
1. Ajouter les paquets
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.10" />
2. Configurez votre .csproj pour AOT:
<PublishAot>true</PublishAot>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>full</TrimMode>
<InvariantGlobalization>true</InvariantGlobalization>
<PublishSingleFile>true</PublishSingleFile>
<StripSymbols>true</StripSymbols>
<OptimizationPreference>Speed</OptimizationPreference>
3. Initialisez SQLite au début de votre Main méthode:
SQLitePCL.Batteries.Init();
4. Construire pour votre plate-forme cible:
dotnet publish -c Release -r linux-x64 --self-contained
5. Testez votre binaire—il devrait simplement fonctionner sans dépendanceM SK2
Conclusion
Faire fonctionner SQLite avec AOT autochtone n’est pas évident, mais une fois que vous connaissez l’incantation magique,SQLitePCLRaw.bundle_e_sqlite3 + Batteries.Init()—it's straightforwardM SK2 La rémunération est substantielle : petits binaires MSC4 démarrage instantané , et la capacité de déployer sur n’importe quelle plate-forme sans dépendance du temps d’exécution
Pour tous ceux qui construisent des outils CLI, passerelles , ou applications de bord avec .NETM SK3 L'OT natif avec SQLite est maintenant une option réalisteMSC4 Le flux de travail GitHub Actions fourni ici automatise l'ensemble du processus de construction multi-platformMST5M ST6juste d'étiqueter un relevé et vousMst7vous avez terminéM st8
Si vous ' êtes nouveau à l’AOT , débutez de petite taille МSK2 convertissez d’abord un outil ou une utilité CLI simple | . se sentez à l'aise avec le processus de construction |, apprenez quels sont les avertissements à attendre |
L’écosystème NET est de plus en plus AOT-friendly. La plupart des bibliothèques modernes fonctionnent soit hors de la boîte, soit disposent d’une documentation claire sur le soutien AOT.
Expérience de mise en œuvre réelle
Je déploie mon AOT-port d’accès compilé à:
- Droplet d'océan numérique (Linux x64) - fonctionne comme un service systématique
- Raspberry Pi 5 (Linux ARM64) - déploiement à bord pour les essais
- Server Windows (Windows x64) - fonctionne comme un service Windows
- Contenants Docker - toutes les variantes x64 et ARMM SK2
Le processus de déploiement est identique partout:
- Télécharger l'archive de GitHub Releases
- Extraire
- Exécuter le programme exécutable
Aucun "installer .NET Runtime" stepM SK3 Aucun infernal de dépendanceMST4 Aucun conflit de versionMSC5 Extraire et exécuter simplement
Pour Docker, mon Dockerfile est gênantment simple:
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"]
L'image qui en résulte est ~130MB (principalement l'image de base Debian). Un conteneur NET traditionnel . serait
Exemple de travail complet
Tout ce que j’ai montré ici provient d’une production réelle.
- Voir le code source: Mostlylucid.BotDetection.Console
- Voir le processus de travail GitHub Actions: console-gatewayM SK1release.yml
- Télécharger les binaires préfabriqués: Publications de GitHub
Le projet est un proxy YARP réversé minimal avec logiciel de détection bot qui signale les signatures à SQLite. Il démontre
- AOT autochtone avec SQLite
- Multi-platform builds via GitHub Actions
- Groupage de bibliothèques autochtones
- Analyse des arguments CLI
- L'exploitation forestière structurée
- Fermeture gracieuse
Il faut le cloner, l’étudier, en faire un modèle pour vos propres projets d’OTA.
Ressources
- Documentation officielle .NET Native AOT
- SQLitePCLRaw GitHub Repository
- Exemple de travail: Mostlylucid Bot Detection Gateway
- Mon GitHub Actions Workflow
Maintenant, allez construire quelque chose petit et rapide.