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
Sunday, 14 December 2025
Se stai cercando di spedire applicazioni .NET come singoli, eseguibili nativi utilizzando Native AOT, probabilmente hai eseguito face-first nella parete SQLite. Ottieni tutto configurato, la generazione completa con successo, e poi si blocca con crash criptico DllNotFoundException errori circa e_sqlite3. Lasciate che vi risparmi le ore di frustrazione che ho passato attraverso e mostrarvi esattamente come ottenere SQLite lavorando con Native AOT attraverso Windows, Linux (tra cui ARM64 Raspberry Pi), e macOS.
Iniziamo con le basi assolute. Se hai mai costruito un'applicazione .NET e ti sei chiesto perché è necessario installare il ".NET Runtime" sui server o perché la tua applicazione console prende un secondo per iniziare la prima volta che lo esegui, AOT è la risposta a questi problemi.
Quando scrivi codice C# e costruisci la tua applicazione, il compilatore non produce codice macchina che la tua CPU può eseguire direttamente. Invece, produce qualcosa chiamato **Lingua intermedia (IL)**Pensalo come a metà strada tra il tuo codice C# e le istruzioni reali della macchina.
Quando esegui la tua app .NET, ecco cosa succede:
Questo è come avere un traduttore che legge la tua ricetta (IL) e la traduce verbalmente in una linea di chef (CPU) mentre stanno cucinando. Funziona, ma c'è in alto:
Nativo AOT gira questo modello sulla sua testa. Invece di tradurre il codice a runtime, traduce tutto al tempo di generazione. Finisci con un singolo file eseguibile che contiene codice macchina reale la tua CPU può eseguire direttamente nessun runtime, nessun traduttore, nessuna attesa.
Pensate ad esso come ottenere un libro di ricette professionalmente tradotto invece di assumere un traduttore dal vivo. Il lavoro è fatto una volta, in anticipo, e il risultato è pronto per l'uso immediatamente.
Ecco cosa ti dà Native AOT:
1. Piccoli eseguibili: 10-30MB invece di 150MB+
La tua app e tutto ciò che serve viene compilato in un piccolo binario. Nessun file runtime separato.
2. Avvio istantaneo: 80% di partenza più veloce del freddo
I miei test: normale .NET ha preso ~800ms per iniziare, AOT ha preso ~150ms. Non c'è tempo di riscaldamento JIT è pronto per l'esecuzione immediata.
3. Dipendenze zero: No .NET runtime required
Puoi copiare il tuo eseguibile su qualsiasi macchina con il sistema operativo giusto (Windows/Linux/Mac) e funziona semplicemente. Nessun prerequisito "installa .NET 9 Runtime."
4. Abbassare l'utilizzo della memoria: Circa il 50% in meno di memoria
Nessun compilatore JIT seduto in memoria. Nella mia applicazione gateway, normale .NET usato 85MB inattivo, AOT usato 42MB.
5. Meglio per ambienti limitati: Funziona dove JIT non può
Alcuni ambienti (alcuni contenitori Docker, iOS, sistemi embedded) non consentono la generazione di codice runtime. AOT funziona ovunque.
AOT non è magico. Stai scambiando la flessibilità runtime per l'ottimizzazione upfront:
1. Nessuna generazione di codice dinamico
Tutto ciò che genera codice al runtime non funzionerà:
System.Reflection.Emit (creando i tipi dinamicamente)La maggior parte del normale codice .NET va bene, ma alcuni framework che si basano pesantemente sulla riflessione hanno bisogno di una configurazione speciale.
2. Build specifici della piattaforma
JIT compilation produce IL che funziona su qualsiasi piattaforma. AOT produce codice macchina nativo per una piattaforma specifica. È necessario costruire separatamente per:
Copriremo l'automatizzazione con le azioni GitHub piu' tardi.
3. Tempi di costruzione più lunghi
Invece di compilare a IL in secondi, AOT compila tutta la strada per codice macchina. Aspettatevi 2-5 minuti invece di 10 secondi. Si tratta di un costo una tantum per benefici permanenti.
4. Alcune caratteristiche hanno bisogno di configurazione supplementare
La serializzazione di JSON, l'Enterprise Framework e qualsiasi cosa che utilizzi un riflesso pesante potrebbe aver bisogno che tu dica esplicitamente al compilatore quali tipi tenere.
Native AOT è perfetto per:
Salta AOT per:
Per gli strumenti CLI, i microservizi, i contenitori e i dispositivi di bordo, i miglioramenti stanno cambiando il gioco. Ma c'è una presa quando si aggiungono i database SQLite specificamente.
Ora che avete capito che cosa è AOT, parliamo del singolo più grande punto dolore: SQLite. Questa è la parete che la maggior parte delle persone ha colpito quando si cerca di utilizzare AOT con applicazioni supportate dal database.
Ecco il tipico viaggio:
Microsoft.Data.Sqlite al tuo progettoPublishAot=true nel tuo .csprojDllNotFoundException: Unable to load DLL 'e_sqlite3' or one of its dependencies
La tua app si blocca prima che possa fare qualcosa.
Per capire il problema, è necessario sapere circa librerie native.
SQLite non è scritto in .NET è scritto in C. È compilato in codice nativo specifico della piattaforma:
e_sqlite3.dll su Windowslibe_sqlite3.so su Linuxlibe_sqlite3.dylib su macOSQuando usa Microsoft.Data.Sqlite in normale .NET, è solo un involucro intorno a questa libreria SQLite nativo. Al runtime, si cerca di carico dinamico il file nativo appropriato per la piattaforma.
Questo funziona bene con normale .NET perché:
Native AOT ha due caratteristiche che si scontrano con l'approccio di SQLite:
1. Rifilatura aggressiva: AOT rimuove qualsiasi codice che pensa che non stai usando. Se non può dimostrare staticamente che avete bisogno di qualcosa, viene eliminato. Caricamento libreria dinamica confonde il trimmer non può vedere la connessione tra il vostro codice e il nativo SQLite DLL.
2. Nessun supporto di carico dinamico: AOT produce un binario autonomo. Si aspetta che tutte le dipendenze native siano esplicitamente collegate al tempo di compilazione, non caricate dinamicamente al runtime.
Il risultato: Microsoft.Data.Sqlite si aspetta di trovare una libreria SQLite nativa al runtime, ma AOT l'ha tagliata via o non sa come impacchettarla correttamente.
Peggio ancora, se hai altri pacchetti NuGet che usano SQLite (come alcune librerie ORM o la mia mostlylucid.ephemeral.complete pacchetto), si può finire con più fornitori di SQLite incompatibili nel tuo albero della dipendenza.
Ogni provider cerca di lavorare in modo diverso:
Il compilatore AOT si confonde su quale usare, e spesso il risultato è che non include nessuno di loro, o peggio, include file in conflitto che non possono lavorare insieme.
Dopo ore di frustrazione, ho trovato la soluzione: SQLitePCLRaw.bundle_e_sqlite3. Questo è uno speciale pacchetto NuGet progettato appositamente per lavorare con AOT.
Aggiungi questi due pacchetti al tuo progetto:
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.10" />
</ItemGroup>
La bundle_e_sqlite3 pacchetto è diverso dai normali fornitori SQLite:
1. Comprende le librerie SQLite native precompilate per ogni piattaforma principale:
2. Queste librerie native sono confezionate in un modo che il compilatore AOT comprende
Il bundle segna esplicitamente le sue dipendenze native in modo che il compilatore AOT sappia di includerle nel binario finale. Nessun caricamento dinamico, nessun runtime alla ricerca di file.
3. È progettato per le costruzioni cross-platform
Un pacchetto funziona per tutte le piattaforme. Non sono necessari pacchetti specifici per le piattaforme o riferimenti condizionali.
Ricordi i due problemi che abbiamo identificato?
Problema 1: il ritaglio di AOT rimuove le librerie che non possono essere usate
Problema 2: AOT non supporta il caricamento dinamico
Il risultato: quando si costruisce per Windows x64, il bundle include e_sqlite3.dll. Quando si costruisce per Linux ARM64, include l'ARM64 libe_sqlite3.soTutto funziona e basta.
Qui è la parte che viaggia verso l'alto 90% della gente che prova ad usare SQLite con AOT, compreso me al mio primo tentativo.
Con .NET normale, il bundle SQLite si inizializza automaticamente la prima volta che si utilizza SQLite. La magia accade dietro le quinte non c'è bisogno di fare nulla.
Con AOT nativo, questa inizializzazione automatica non funziona. Il compilatore AOT non può vedere il codice di avvio automatico (sembra codice inutilizzato e viene rifilato), quindi si deve inizializzare manualmente il bundle all'inizio della tua applicazione.
Ecco la linea magica di cui hai bisogno:
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();
}
}
Batteries.Init() Fare?Questo metodo dice al bundle SQLite di:
Microsoft.Data.Sqlite e la libreria nativaSi chiama "Batterie" perché è il "batterie incluse" bundle tutto ciò di cui avete bisogno è confezionato insieme.
Metti Batteries.Init() come la prima riga nel tuo Main metodo, prima:
Pensate ad esso come collegare un dispositivo prima di provare ad accenderlo. Se si tenta di utilizzare SQLite prima di chiamare Init(), avrai ancora il DllNotFoundException anche se la DLL è correttamente in bundle nella vostra applicazione.
Se dimentica di chiamare Batteries.Init(), la tua app:
DllNotFoundException: Unable to load DLL 'e_sqlite3' or one of its dependencies
Questo è confuso perché la DLL è Ho perso due ore per questo errore, non essere come me.
Qui c'è un pieno .csproj configurato per AOT nativo multipiattaforma con SQLite:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.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>
Lasciatemi scomporre ciò che ciascuna di queste impostazioni fa:
PublishAot=true
Questo è il master switch che consente la compilazione di Native AOT. Senza questo, si ottiene normale comportamento .NET (JIT compilazione). Con questo, si ottiene in anticipo di tempo compilato codice nativo.
PublishTrimmed=true e TrimMode=full
Questi dicono al compilatore di rimuovere tutto il codice che non state usando. Pensi ad esso come pulire il vostro garage prima di muoversi perché impacchettare le cose che non avete bisogno?
PublishTrimmed=true consente il ritaglioTrimMode=full significa "sii aggressivo, rimuovi tutto quello che puoi"Attenzione: Questo può rompere il codice che utilizza il riflesso pesante (come alcuni serializzatori JSON o ORM) perché il trimmer non può sempre vedere quello che si sta usando attraverso il riflesso. Copriremo come gestire questo in seguito.
InvariantGlobalization=true
Questo rimuove tutti i dati specifici della cultura dai formati di app, simboli di valuta, regole di ordinamento del testo per diverse lingue. Salva 5-10MB.
Imposta solo questo a true se la tua app:
Se stai costruendo uno strumento CLI o un gateway API che non si preoccupa della localizzazione, questo è un risparmio gratuito. Se stai costruendo qualcosa che deve formattare le date per gli utenti francesi o ordinare correttamente il testo turco, saltare questa impostazione.
PublishSingleFile=true
Bundles tutto in un unico file eseguibile. Invece di avere:
myapp.exe
myapp.dll
System.Text.Json.dll
... 50 more files
Tu ottieni solo
myapp.exe
Molto piu' facile da distribuire.
StripSymbols=true
I simboli di debug aiutano i debug a mostrare nomi variabili e numeri di riga durante il debug. Sono utili durante lo sviluppo, ma aggiungono diversi megabyte al binario finale.
Questa impostazione li rimuove. L'applicazione funziona esattamente lo stesso, solo più piccolo.
OptimizationPreference=Speed
Questo dice al compilatore cosa dare priorità quando si prendono le decisioni:
Speed: Rendere veloce (binario leggermente più grande, ma migliori prestazioni)Size: Rendere piccolo (leggermente più lento, ma minima dimensione binaria)Per la maggior parte delle applicazioni, Speed è la scelta giusta. La differenza di dimensioni è di solito solo 2-3MB, ma la differenza di prestazioni può essere evidente.
RuntimeIdentifiers
Questo dichiara quali piattaforme si desidera supportare. Esso non li costruisce tutti dice solo strumento "questi sono obiettivi validi."
Identificatori disponibili:
win-x64: Windows 64-bit (Intel/AMD)win-arm64: Windows ARM64 (Surface Pro X, ecc.)linux-x64: Linux 64-bit (Ubuntu, Debian, RHEL, ecc.)linux-arm64: Linux ARM64 (Raspberry Pi 4/5, AWS Graviton)osx-x64: macOS Intel (vecchio Mac)osx-arm64: macOS Apple Silicon (M1/M2/M3 Macs)Costruisci una piattaforma alla volta usando dotnet publish -r linux-x64, per esempio.
Ricordate come ho detto AOT richiede build specifici per la piattaforma? È necessario compilare separatamente per Windows, Linux x64, Linux ARM64, macOS Intel, e macOS Apple Silicon. Fare questo manualmente sarebbe noioso ma possiamo automatizzare.
Se non hai familiarità, GitHub Actions è un servizio gratuito CI/CD (Integrazione continua/Distribuzione continua) integrato in GitHub. Consente di eseguire attività automatizzate ogni volta che si preme il codice o si crea un tag di rilascio.
Pensaci come se avessi un server build che:
v1.0.0Tutto questo funziona sui server di GitHub non è necessario mantenere alcuna infrastruttura. Per i progetti open-source e piccoli progetti personali, è completamente gratuito.
Ecco un flusso di lavoro GitHub Actions che costruisce automaticamente per tutte le principali piattaforme:
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
Se lo YAML sembra intimidatorio, ecco cosa fa in inglese semplice:
1. Trigger (on: push: tags)
Il flusso di lavoro viene eseguito quando si preme un tag Git che inizia con v (come v1.0.0, v2.3.1). Questo è il modo standard per contrassegnare le versioni di rilascio.
2. Strategia della matrice
Questa è la parte intelligente. Invece di scrivere cinque flussi di lavoro separati, definiamo un matrice di edifici:
os (runner machine) e runtime (piattaforma di destinazione)Quindi quando spingi v1.0.0, GitHub simultaneamente:
3. Passi in ogni costruzione
Ogni piattaforma costruisce gli stessi passaggi:
dotnet publish con flag AOT per quella specifica piattaforma4. Il risultato
Dopo tutti i build completi, hai cinque artefatti (binari) pronti per essere distribuiti. Puoi scaricarli dall'esecuzione di Azioni o usare un secondo lavoro per creare automaticamente una release GitHub (non mostrata in questo frammento, ma facile da aggiungere).
Ecco una cosa che mi ha confuso per ore: PublishSingleFile=true Solo pacchetti .NET codice. Librerie native come SQLite e_sqlite3.dll Restate separati.
Ecco perché il passo "Crea pacchetto di distribuzione" è così 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
La 2>/dev/null || true parte significa "se non ci sono file corrispondenti a questo modello, non fallire semplicemente continuare." Questo consente lo stesso script di lavorare su tutte le piattaforme.
Cosa distribuisci:
myapp.exe + e_sqlite3.dll (impacchettato insieme in un ZIP)myapp + libe_sqlite3.so (impacchettato insieme in un tar.gz)myapp + libe_sqlite3.dylib (impacchettato insieme in un tar.gz)Gli utenti estraggono l'archivio ed eseguono l'eseguibile. La libreria SQLite nativa si trova accanto all'eseguibile, e il bundle lo trova automaticamente al runtime.
Notare la workflow_dispatch grilletto in cima:
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: 'Version to publish'
required: true
Questo consente di attivare manualmente la generazione dall'interfaccia web di GitHub senza premere un tag. Utile per testare il flusso di lavoro o creare build beta.
Linux ARM64 build richiede un'attenzione particolare. È necessario utilizzare strumenti di cross-compilation e specificare il corretto objcopy strumento:
# 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
Senza ObjCopyName parametro, il linker non riesce con errori criptici sui formati di file non riconosciuti.
Ecco cosa ho ottenuto con il gateway di rilevamento bot basato su YARP di produzione con registrazione middleware e SQLite. Questo è un progetto reale che è possibile download da GitHub.
Queste sono le dimensioni effettive dei file dalle mie versioni di GitHub, non stime teoriche:
| Piattaforma | Eseguibile | SQLite Native DLL | Dimensione archivio totale |
|---|---|---|---|
| Finestre x64 | 9.2MB | +1,7MB | 10,9MB (ZIP) |
| Linux x64 | 10,8MB | +1,6MB | 12,4MB (tar.gz) |
| Linux ARM64 | 9,9MB | +1,5MB | 11,4MB (tar.gz) |
| Intel macOS | 11.2MB | +1.8MB | 13,0MB (tar.gz) |
| macOS ARM64 | 9,8MB | +1,7MB | 11,5 MB (tar.gz) |
Confronta questo a implementazioni .NET 9 auto-contenute a 130-150MB per piattaforma. Stiamo parlando di un 10-12x riduzione delle dimensioni.
La ripartizione:
Entrambi i file devono essere distribuiti insieme, ma sono ancora drammaticamente più piccoli delle implementazioni .NET tradizionali.
Cold start (prima richiesta servita) su un modesto VPS Linux:
Ciò è estremamente importante per:
Memoria idle (gateway running, senza traffico):
Sotto carico (1000 richieste/secondo):
Abbassare la memoria significa:
Batteries.Init()Questo è l'errore #1. Anche con il bundle correttamente fatto riferimento, dimenticando di chiamare SQLitePCL.Batteries.Init() all'inizio del vostro Main metodo causerà crash di runtime.
La correzione:
public static void Main(string[] args)
{
SQLitePCL.Batteries.Init(); // FIRST LINE
// ... rest of your code
}
Questo è l'errore numero 2 e mi ha colpito due volte. PublishSingleFile=true NON raggruppa le DLL native SQLite nel tuo eseguibile. Devi distribuirle insieme al tuo eseguibile.
Cosa succede se dimentica:
DllNotFoundExceptionLa correzione:
Quando imballate il vostro rilascio, includono sempre:
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)
Nelle azioni di GitHub o negli script di implementazione:
# 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
Gli utenti dovrebbero estrarre l'archivio ed eseguire l'eseguibile. La libreria nativa deve essere nella stessa directory.
winsqlite3 FornitorePotrebbe vedere raccomandazioni da usare SQLitePCLRaw.provider.winsqlite3 su Windows per utilizzare il sistema operativo fornito SQLite. Non. Funziona solo su Windows, richiede l'inizializzazione manuale, e rompe le build cross-platform. bundle_e_sqlite3.
Se stai usando Entity Framework Core con SQLite, riceverai avvisi di trim (IL2026, IL3050). Questi di solito sono sicuri da sopprimere per il provider SQLite di EF Core, ma testare accuratamente:
<PropertyGroup>
<NoWarn>$(NoWarn);IL2026;IL3050</NoWarn>
</PropertyGroup>
Meglio ancora, considerare l'utilizzo di Dapper o raw ADO.NET con SQLite per le applicazioni AOT sono più AOT-friendly.
Su Windows, Native AOT richiede il linker MSVC di Visual Studio. Se si costruisce al di fuori di un Prompt dei comandi di sviluppo, si otterrà errori circa vswhere.exe. Le azioni GitHub gestiscono automaticamente questa operazione, ma per i build locali, usano:
O inizializza l'ambiente nel tuo script di compilazione:
& "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\Launch-VsDevShell.ps1" -Arch amd64
dotnet publish -c Release -r win-x64
Native AOT è perfetto per:
Evitare AOT per:
Se hai letto l'intero post e vuoi solo una lista di controllo da seguire, eccoti qui:
1. Aggiungere i pacchetti:
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.10" />
2. Configura il tuo .csproj per AOT:
<PublishAot>true</PublishAot>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>full</TrimMode>
<InvariantGlobalization>true</InvariantGlobalization>
<PublishSingleFile>true</PublishSingleFile>
<StripSymbols>true</StripSymbols>
<OptimizationPreference>Speed</OptimizationPreference>
3. Inizializzare SQLite all'inizio del vostro Main metodo:
SQLitePCL.Batteries.Init();
4. Costruisci per la tua piattaforma di destinazione:
dotnet publish -c Release -r linux-x64 --self-contained
5. Provare il tuo κανονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονον ονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονονοννννννονονονονονονονοννννννννννννννονονονον
Ottenere SQLite che lavora con Native AOT non è ovvio, ma una volta che si conosce l'incantesimo magicoSQLitePCLRaw.bundle_e_sqlite3 + Batteries.Init()Il payoff è sostanziale: piccoli binari, startup istantanea, e la possibilità di distribuire su qualsiasi piattaforma senza dipendenze runtime.
Per chiunque costruisca strumenti CLI, gateway o applicazioni di bordo con .NET, Native AOT con SQLite è ora un'opzione realistica. Il flusso di lavoro GitHub Actions fornito qui automatizza l'intero processo di compilazione multi-piattaforma.
Se sei nuovo a AOT, iniziare piccolo: convertire un semplice strumento CLI o utilità prima. Ottenere conforto con il processo di compilazione, imparare quali avvertimenti aspettarsi, e capire i limiti. Una volta che hai le basi giù, è possibile affrontare applicazioni più complesse.
L'ecosistema .NET è sempre più AOT-friendly. La maggior parte delle librerie moderne o lavorare fuori dalla scatola o avere una chiara documentazione sul supporto AOT. Il futuro è nativo, ed è più veloce di quanto si pensi.
Dispiegare il mio AOT-compiled gateway a:
Il processo di spiegamento è identico ovunque:
Nessun "installare .NET Runtime" passo. Nessuna dipendenza inferno. Nessun conflitto di versione. Basta estrarre ed eseguire.
Per Docker, mio Dockerfile è imbarazzantemente semplice:
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'immagine risultante è ~130MB (per lo più l'immagine Debian base). Un contenitore .NET tradizionale sarebbe 200-250MB.
Tutto quello che ho mostrato qui proviene da un progetto vero e proprio, pronto alla produzione.
Il progetto è un reverse proxy minimo YARP con bot detection middleware che registra le firme su SQLite. Dimostra:
Clonatelo, studiatelo, usatelo come modello per i vostri progetti AOT.
Ora vai a costruire qualcosa di piccolo e veloce.
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.