Multiplatform AOT med SQLite: Hur få det att fungera
Inleiding
Native AOT lovar att göra din .NET-applikation till en liten, pytteliten, självständig, implementerad version som börjar omedelbart och kör varsomhelst. DllNotFoundException krockar , kämpar med transitiva beroenden, och undrar varför PublishSingleFile=true kan inte bundna allt i en enda file. Det här är den kompletta guiden till att få SQLite att jobba med Native AOT. , bygger på flera platformer, , och automatiserar utsläppen med GitHub actions.
Vad är AOT?
Låt oss börja med de absoluta grunderna.
Hur .NET fungerar normalt (JIT Compilation)
När du skriver C# kod och bygger din application , producerar kompilatorn inte maskinkod som din CPU kan direkt utföra Intermediat språk (IL)—se det som halvvägs mellan ditt C-kode och verkliga maskininstruktioner
När du kör din .NET app , här' är vad som händer?
- Din dator laddar .NET Runtime ( ett separat program
- Runtime läser din IL-kod
- Bara-In-Time (JITM SK3 compiler konverterar IL till inhemska maskinkod medan din app körs
- Din CPU utför äntligen maskinens kod
Det är som att ha en översättare som läser ditt recept (, IL, ), och översätter det verbalt till en kock. ♫ (, CPU, ), rad för rad medan de ♫
- Runtimet för .NET är 50-150MB av extra filer som du behöver implementera
- JIT compilern tar tid att översätta koden ( Varför är din app långsam den första gången)
- JIT kompilatorn själv sitter i minnet medan din app körs
Enter AOT (Ahead-OfM SK2Time CompilationMSC3
Infödda AOT sätter den här modellen på huvudet.. Istället för att översätta din kod på starttid, översätter den allt vid byggtid. Du får till slut en enda lösbar file som innehåller den verkliga maskinkod som din CPU kan köra direkt
Tänk på det som att få en professionellt översatt receptbok istället för att anställa en direkt översättare.
Spelet---Changer i vinsterna
Här är vad inhemska utbildningsprogram ger dig.
1. Små utförbara: 10-30MB istället för 150MBM SK3
Din app och allt som den behöver kompileras till ett litet binärt program.
2. Omedelbart starta: 80% snabbare kylangång
Mina tester: normala .NET tog ~800ms till att starta M SK3 AOT tog ♫ ~150msM SK5 Det finns inga JIT varma apparater - uppgångstid ♫— maskinkoden är redo att köra direkt ♫
3. Null beroende: Nej .NET körtid krävs
Du kan kopiera din Executable till vilken maskinen som helst med den rätta OS: n.
4. lägre minnesmängd: Men mindre minne
Ingen JIT-kompilator som sitter i minnet. I min Gateway-applikation, normal | . | NET användes |85 | MB avlägsna | МSK4 | AOT användes
5. Bra för begränsade miljöer: Werk där JIT kan
Några miljöer (bestämda Dockercontainer , iOS , , inbyggda system
Världshandeln
AOT är "'", det inte är magiskt, ".", du "'", är flexibilitet i affärernas starttid för förstahandsoptimering.
1. Ingen dynamisk kod generering
Allt som genererar kod vid starttid kommer att fungera
System.Reflection.Emit( skapa typer dynamisktM SK1- Dynamik assemblage laddande ( laddande DLLs vid starttid)
- Några flotta reflektionstricks
De flesta normala .NET-koderna är bra, men vissa ramverk som är beroende av reflektion behöver särskild konfiguration.
2. Plattform -specifika byggningar
JIT kompilation producerar IL som kör på vilken plattform som helst. AOT producerar inhemska maskinkod för en specifik plattform. Du behöver bygga separat för
- Windows x64
- Linux x64
- Linux ARM64 (Raspberry Pi)
- macOS Intel
- macOS Apple Silicon
Vi kan automatisera detta med GitHub Actions senare.
3. längre byggtid
Istället för att kompilera till IL i sekunder, AOT kompilerar hela vägen till maskinens kod
4. Några egenskaper behöver extra konfiguration
JSON serialisering, Enhetens ramverk , och allt som använder stor reflektion behöver du explicit säga till kompilatorn vilka typer man ska behålla
När ska du använda AOT?
Infödda AOT är perfekt för:
- CLI-verktyg: Kommandon -inline-användbarheter där det är viktigt att starta direkt
- Mikrotjänster: Mindre Dockerbilder , snabbare skalering i Kubernetes
- Serverlös/Lambda: kalla starttid påverkar direkt din Rechnung
- Kantdelar: Raspberry Pi, IoT-apparater med begränsade resurser
- Gateway-applikationer: Omväntade proksier , API-portaal med hög hastighet
- Desktop-verktyg: La iväg en enda .exe utan att | " | installera | . | NET första | " | steg
Skip AOT för:
- Vanliga webapps där starttid inte spelar någon roll
- Apps som använder ett tungt Entity Framework med många migrationer
- Plugin-system som laddar DLLs dynamiskt
- Allt som genererar kod vid starttid
För CLI-verktyg: ,, mikrotjänster, ,, behållare, , och randapparater,,, är förbättringarna spel, M SK4, förändringar, .. Men det finns ett problem med ' när du lägger till databaser,
SQLiteproblemet
Nu när ni förstår vad AOT är, låt oss prata om den enskilt största smärtpunkten.
Vad händer ( Den frustrerande upplevelsen)
Här är den typiska resan
- Du lägger till
Microsoft.Data.Sqlitetill ditt projekt - Du konfigurerar
PublishAot=truei era.csproj - Byggnaden slutar utan fel— allt ser bra ut!
- Du kör den utförbara och får omedelbart:
DllNotFoundException: Unable to load DLL 'e_sqlite3' or one of its dependencies
Appen kraschar innan den kan göra något
Understanding Native Libraries (A Quick Detour
För att förstå problemet, behöver du veta inhemska bibliotek.
SQLite är: ' inte skriven i NET, . är skrivet i C och ' är skompilerad på plattformen.
e_sqlite3.dllpå Windowslibe_sqlite3.sopå Linuxlibe_sqlite3.dylibpå macOS
När du använder Microsoft.Data.Sqlite i det normala .NET, är det bara ett wrapper runt den här inhemska SQLite biblioteken . När det körs dynamiskt ladda den lämpliga moderen för din plattform.
Detta fungerar bra med normala .NET eftersom
- Runtime kan ladda bibliotek dynamiskt
- NuGet-paket kan inkludera moder bibliotek för alla plattformar
- Den högra blir väljad vid starttid
Varför bryter AOT detta?
Infödda AOT har två egenskaper som kolliderar med SQLite
1. Aggressiv trimning: AOT tar bort vilken kod som den tror att du använder ' du använder inte . Om det inte kan bevisa statiskt att du behöver något, så blir det löscht M SK5 Dynamiklaadning av biblioteket förvirrar trimmern MSC6 det inte klarar av kopplingen mellan din kod och den inhemska SQLite DLL
2. Ingen dynamiksladeringssupport: AOT producerar ett självbyggt binärt system - det förväntar sig att alla infödda beroenden är explicitt länkade vid kompileringstid
Resultatet: Microsoft.Data.Sqlite förväntar sig att hitta en inhemsk SQLite bibliotek vid starttid, men AOT har antingen klippt bort den eller inte vet hur man bundlar den ordentligt.
Den förvandliga beroendens mardröm
Ännu värre, om du har andra NuGet paket som använder SQLite ( som vissa ORM bibliotek eller min mostlylucid.ephemeral.complete package), du kan hamna med flera okompatibla SQLite-provider i ditt beroendeträd.
Varje leverantör försöker jobba på ett annat sätt:
- Man kan förvänta sig att OS-ser ett SQLite ( endast WindowsM SK2
- En annan skulle kunna byta ihop sin egen SQLite
- En annan kanske använder en annan inhemsk bibliotekversion
AOT- kompilatorn blir förvirrad om vilken som ska användas, och ofta så är det så att den inte inkluderar någon av dem.
Lösningen: Använd paketet
Lösningen är SQLitePCLRaw.bundle_e_sqlite3— ett speciellt NuGet-paket som är speciellt utformat för att fungera med AOT
Lägg dessa två paket till ditt projekt:
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.10" />
</ItemGroup>
Vad gör paketet?
Den bundle_e_sqlite3 paketet skiljer sig från vanliga SQLite-provider:
1. Det inkluderar pre-kompilerade inhemska SQLite bibliotek för varje större platform
- Windows (x64, x 86, ARMM SK3
- Linux (x 64, ARM64, muslM SK3baserade distros som AlpineMSC4
- macOS (x64 Intel, ARMM SK3 Apple SiliconMSC4
2. Dessa inhemska bibliotek är paketerade på ett sätt som AOT kompilatorn förstår
Paketet markerar explicit dess ursprungliga beroenden så att AOT-kompilaren vet att inkludera dem i den slutgiltiga binären.
3. De designade för cross---platform-byggnader
Ett paket fungerar för alla plattformar. Du behöver inte ' behöver inte plattformen- specifika paket eller villkorliga referensramar
Varför det här fungerar
Kom ihåg de två problem vi identifierade
Problemet 1: AOT 's trimming tar bort bibliotek som den inte kan se som används
- Lösningen: Paketet deklarerar explicit sina inhemska bibliotek som byggade tillgångar som måste inkluderas
Problemet 2: AOT inte supporterar dynamiska lade
- Lösningen: Paketet länkar statiskt till SQLite biblioteken vid kompileringstid
Resultatet : när du bygger för Windows x64, inkluderar paketet e_sqlite3.dll. När du bygger för Linux så är det ARM libe_sqlite3.so. Allt fungerar
Kritiskt: Initialisera paketet M SK1Ska 'T hoppa över detta!)
Här är "'", den delen som reser upp till МSK1 av människor som försöker använda SQLite med AOT, inklusive mig på mitt första försök.
Med normala .NET, SQLite-bundeln initialiseras automatiskt den första gången du använder SQLite
Med inhemska AOT, den här automatiserade initialiseringen fungerar inte ' det funkar inte måste initialisera paketet i början av din application.
Här är den magiska gränsen du behöver
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();
}
}
Vad gör Batteries.Init() Do?
Den här metoden säger till SQLite-bundlet att:
- Hitta den korrekta ursprungliga SQLite biblioteket för din nuvarande plattform
- Lade in den i minnet
- Dra upp alla kopplingar mellan
Microsoft.Data.Sqliteoch den ursprungliga biblioteket
Det kallas för """, batterier, " eftersom det är paketet med batterierna. Allt man behöver är paketerat tillsammans.
Var man ska sätta det
Sätt Batteries.Init() som den allra första linjen i era Main metod , före:
- Att skapa applikationsbyggaren
- Att konfigurera beroende-injictioner
- Att öppna alla databaseuppkopplingar
- Att läsa konfigurationsfiler som kan använda SQLite
Tänk på det som att koppla in en apparat innan du försöker slå på den. Om du försöker använda SQLite innan du ska ringa Init(), du ' får fortfarande DllNotFoundException även om DLL är korrekt bundlat i din application.
Vad händer om du glömmer
Om du glömmer ringa Batteries.Init(), din app kommer
- Att bygga framgångsrikt (kompilaren vann'viker att varna dig
- Börja köra
- Krockar när den försöker använda SQLite med:
DllNotFoundException: Unable to load DLL 'e_sqlite3' or one of its dependencies
Det är förvirrande eftersom DLL är bundled in i din application—it's bara inte initialiserad
Komplett projektkonfiguration
Här är "'". .csproj konfigurerad för multi-platform Native AOT med 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>
Nyckelregler förklarade (Vux engelskan)
Låt mig bryta ner vad varje av dessa inställningar gör
PublishAot=true
Detta är den mästerliga strömbrytaren som gör det möjligt att kompilera AOT som är infödda.. Utan detta får man normala NET-beteenden, (, JIT-kompilationer och ).. Med det här får man fram fram till
PublishTrimmed=true och TrimMode=full
De säger till kompilatorn att ta bort vilken kod som helst du inte använder. Tänk på det som att städa ut din garage innan du flyttar. Varför packa saker du inte behöver?
PublishTrimmed=truemöjliggör trimmingTrimMode=fullbetyder " vara aggressiv , ta bort allt du kan
Varning: Det här kan bryta koden som använder tung reflektion ( som vissa JSON serialisatorer eller ORMs ) eftersom trimmern inte alltid kan se vad du userar via reflektionen
InvariantGlobalization=true
Det tar bort alla kulturer- specifika data från din app— datumformat , valutasymboler M SK3 regler för att sortera text på olika språk
Sätt bara det här till true om din app:
- Bara använder engelska
- Brakar inte formatering av datum och tid
- Bara använder vanliga strings jämförelser (byte-byM SK2byte≥)
Om du bygger ett CLI-verktyg eller API-portaal som inte bryr sig om lokalisering, så är det gratis.
PublishSingleFile=true
Bundlar allt i en enda lösbar file. Istället för att ha
myapp.exe
myapp.dll
System.Text.Json.dll
... 50 more files
Du får bara:
myapp.exe
Mycket enklare att tillämpa.
StripSymbols=true
Debug-symboler hjälper debugger att visa variable namn och linjer när de gör ont.
Det här sättet tar bort dem. Appen kör exakt samma sak, bara mindreM SK2
OptimizationPreference=Speed
Detta säger till kompilatorn vad som ska prioriteras när man fattar beslut
Speed: Gör det snabbare | ( | Lite större binärar |, | men bättre resultat |Size: gör den liten ( lite långsammare МSK2 men minimal binär storlek
För de flesta applikationer, Speed är det rätta valet. Storleksskillnaden är vanligtvis bara 2-3MBM SK2 men skillnaden i prestation kan vara märkbar
RuntimeIdentifiers
Det deklarerar vilka plattformar du vill stödja.
Available identifiers:
win-x64: Windowswin-arm64: Windows ARMlinux-x64: Linux | 64- | bit | ( | Ubuntu | , | Debian | , | RHEL |, | etclinux-arm64: Linux ARM64 (Raspberry Pi ♫ ♫ 4/5, ♫ AWS Graviton ♫osx-x64: macOS Intelosx-arm64: macOS Apple Silicon
Du bygger en plattform i taget genom att använda dotnet publish -r linux-x64, till exempel
Byggnader för flera plattformar
Kom ihåg hur jag sa att AOT kräver plattform- specifika konstruktioner ? Du behöver kompilera separat för Windows, Linux xM SK3 Linux ARMMska4 macOS IntelMske5 och macOS Apple SiliconMskar6 Att göra detta manuell skulle vara tråkigtMskan7 men vi kan automatisera detMkan8
Vad är GitHub Actions?
Om du inte känner till: ', , GitHub Actions är en gratis CI-version.
Tänk på det som att ha en byggserver
- Tittar på ditt GitHub-rumslag
- När du trycker på ett tag som
v1.0.0 - Den snurrar upp automatiskt Windows, ,, Linux och macOS virtuella maskiner.
- Bygger din app för alla Plattformar parallellt
- Creates a GitHub Release with all the binaries attached
Allt detta körs på GitHub.
Den kompletta byggprocessen
Här är "'", en GitHub Actions (Workflow) som bygger på alla stora platformer automatiskt.
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
Hur den här arbetsgången fungerar ( steg för steg
Om YAML ser skrämmande ut så är det vad den gör på engelska
1. Trigger on: push: tags)
arbetsflödet körs när du trycker på en Git-tag som börjar med v ( som v1.0.0, v2.3.1). Det här är det vanliga sättet att markera releaseversioner
2. Matrixstrategi
Det här är den smarta delen, i stället för att skriva fem separata arbetsflöden, definierar vi matrix av byggnader:
- Varje byggnad får en annan
os( löpare maskinM SK1 ochruntime(target platform - GitHub Actions kör alla fem byggningar parallellt
- Var och en producerar en artefakt (kompilerad binär )
Så när du trycker v1.0.0, GitHub samtidigt:
- Spinar upp en Ubuntu-maskin för att bygga Linux x64 och ARM64
- Spinar upp en Windows maskin för att bygga Windows x64
- Spinar upp en macOS maskin för att bygga macOS x64 och ARM64
3. steg i varje byggnad
Varje plattform gör samma steg
- Checkout-kod: Tar din källkod från arkiven
- Installation .NET: Installerar .NET 10 SDK
- Installera ARM64 verktyg (Linux ARM
- publicera: kör
dotnet publishmed AOT-flagger för den specifika plattformen - Uploada artefakt: sparar kompilerade binära så att nästa jobb kan komma åt dem
4. Resultat
Efter att alla byggningar är färdiga, har du fem artefakter. ( binärar, ) redo att distribuera, . Du kan ladda ner dem från Running: Actions, or använda en andra uppgift för att skapa en GitHub-utsläpp automatiskt.
Kritiskt: Inhemska bibliotek är inte bundlade
Här är något som förvirrade mig i timmar. PublishSingleFile=true bara paket .NET kod. Infödda bibliotek som SQLite e_sqlite3.dll stanna separat.
Det är därför steget "Create distribution package " är så viktigt
# 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
Den 2>/dev/null || true del betyder " om det inte finns några filer som matchar den här mönstret , inte, ' inte misslyckas
Vad ni delar ut:
- Windows:
myapp.exe+e_sqlite3.dll( sammanbundna i en ZIP - Linux:
myapp+libe_sqlite3.so(bundled together in in a tar - macOS:
myapp+libe_sqlite3.dylib(bundled together in in a tar
Användare extraherar arkiven och kör den gjorda versionen. Den inhemska SQLite biblioteket sitter bredvid den drivbara versionen, och paketet hittar den automatiskt vid starttid
Varför är manuellt triggaring användbart
Notera att workflow_dispatch löser på toppen:
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: 'Version to publish'
required: true
Det låter dig aktivera byggande manuell från GitHubs webbgränssnitt, utan att trycka på tag.
ARM64 Cross-Compilation
Linux ARM64 bygger kräver särskild uppmärksamhet. Du behöver crossM SK2 kompileringsverktyg och måste specifikera den korrekta objcopy verktyg:
# 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
Utan ObjCopyName parameter, länkaren misslyckas med kryptiska fel om okända format
Resultat i världen
Här är vad jag åstadkommit med min produktion YARP-baserad Bot-detection gateway med middleware och SQLite-loggning ladda ner från GitHub.
Binära storlekar (Vom verkliga GitHub-ut releases)
Det här är faktiska filestorlekar från mina GitHub-ut releases, inte teoretiska uppskattningar :
| Plattform | Rakettbart | SQLite Native DLL | Totala arkivstorlek | |||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Windows x 10.9MB (ZIP | ||||||||||
| Linux x 12.4MB (tar | ||||||||||
| Linux ARM 11.4MB (tar | ||||||||||
| macOS Intel 13.0MB (tar | ||||||||||
| macOS ARM 11.5MB (tar |
Vergleicha detta med självbevarade --deployment på 130-150MB per plattform. Vi talar om 10-12x minskning av storlek.
Breakdown: :
- Det viktigaste som kan göras: : Koden du kompilerade + | . | NET runtime
- Infödda DLL: SQLite-databassdrivaren ( skriven i C
Båda plikter måste fördelas tillsammans, men de är ännu dramatiskt mindre än traditionella NET-implementationer.
Startprestential
Cold start (first request served) on a modest Linux VPS
- Själva | - | med |. | NET: ~800ms
- Infödda AOT: ~150ms
- förbättring: 81% snabbare
Detta spelar enorm roll för:".
- Serverlös/Lambda-funktioner | ( | Du betalar per millisekunder
- Container som skalar upp och ned frequently
- CLI-verktyg där varje invocation börjar ny
minnesanvändning
Mindest minne ( startväg igång , ingen trafik
- Själva | - | med |. | NET: 85MB
- Infödda AOT: 42MB
- förbättring: 51% minskning
Under belastning (1000 begäranM SK1sekunder ):
- Själva | - | med |. | NET: ~320MB
- Infödda AOT: ~180MB
- förbättring: 44% minskning
lägre minne betyder:
- Fler behållare per värd
- billigare molntjänster
- Möjligheter på resurser-begränsade enheter (Raspberry PiM SK2 IoT )
Vanliga grottor
1. Att glömma Batteries.Init()
Det här är #1 felet SQLitePCL.Batteries.Init() i början av ditt Main metoden kommer att orsaka avloppsbrott.
Fixen:
public static void Main(string[] args)
{
SQLitePCL.Batteries.Init(); // FIRST LINE
// ... rest of your code
}
2. Distribuerar inte inhemska bibliotek
Det här är ett misstag #2, och det fick mig två gånger PublishSingleFile=true bundlar inte infödda SQLite DLLs i din gjorda version.. Du måste fördela dem tillsammans med din drivbara version.
Vad händer om du glömmer
- Appen bygger bra
- Det funkar bra på din dev-maskin (där SQLite kanske redan är installerad)
- Det kraschar på produktionen med
DllNotFoundException
Fixen:
När du packar ditt utsläpp, inkluderar du alltid:
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)
I dina GitHub-aktioner eller deployment skript:
# 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
Användare borde extrahera arkiven och köra den gjorda versionen.
3. Användande winsqlite3 Tillhandahållare
Du kanske ser tillämpningar att använda SQLitePCLRaw.provider.winsqlite3 på Windows för att använda OS: n bundle_e_sqlite3.
4. Trimvaror med EF Core
Om du använder Entity Framework Core tillsammans med SQLite, får du ",", du får trim warnings , trim warning , och de är vanligtvis säkra att trycka ned på för EF Core, ' och SQLite-leverantören.
<PropertyGroup>
<NoWarn>$(NoWarn);IL2026;IL3050</NoWarn>
</PropertyGroup>
Ännu bättre, överväg att använda Dapper eller rå ADO.NET med SQLite för AOT-applikationer
5. Missande visuella Studio-verktyg
På Windows requires Native AOT Visual Studio, MSVC-linker, . om du bygger utanför en Developer Command Prompt vswhere.exe. GitHub Actions hanterar det här automatiskt , men för lokala byggnader
- Utvecklingskommandopropt för VS 2022
- Developer PowerShell för VS 2022
Eller att initialisera miljön i ditt bygg skript:
& "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\Launch-VsDevShell.ps1" -Arch amd64
dotnet publish -c Release -r win-x64
När använda det ursprungliga AOT
Infödda AOT är perfekt för:
- CLI-verktyg: Omedelbart starta
- Mikrotjänster och behållare: Mindre bilder , snabbare skalering
- Kantdelar: Raspberry Pi, IoT-apparater med begränsade resurser
- Serverlös/Lambda: Cold start time is critical
- Gateway/proxy-applikationer: Hög genomspetsning , låg överhead
undvika AOT för:
- Heavy Entity Framework Core-användbarhet ( mängder av migrationer , komplexa queryr
- Reflektioner-svåra tillämpningar
- Dynamiska pluginsystem
- Programme som behöver drivtidskod generering
Vinnig Start-kontrolllista
Om du' har läst hela den här artikeln och bara vill att en checklist ska följa
1. Lägg till paketen
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.10" />
2. Konfigurera din .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>
3. Initialisera SQLite i början av ditt Main metod:
SQLitePCL.Batteries.Init();
4. Att bygga för ditt målplattform
dotnet publish -c Release -r linux-x64 --self-contained
5. Testa ditt binära system
Slutsatsen
Att få SQLite att jobba med inhemska AOT är inte självklart, men när du väl känner till magincantationenSQLitePCLRaw.bundle_e_sqlite3 + Batteries.Init()—, it, ', straightforward, ., lönsamheten är substantiv, :, små binärsystem, ,, snabbstart, m sk5 och möjligheten att användas på vilken plattform som helst utan avstånd från starttid.
För alla som bygger CLI-verktyg, grindvägar , eller randanwendunger med |. |NET | , | Infödda AOT med SQLite är nu en realistisk alternativ |
Om du är ny på AOT, så startar du en liten modell, :, konverterar först ett enkelt CLI-verktyg eller -användbart. ., må bra med byggprocessen, ,, lär dig vilka varningar man ska förvänta sig, M SK5, och förstår begränsningarna,.. När du väl har gått igenom grunderna, MSC8, kan du hantera mer komplexa tillämpningar.
Ecosystemet är allt mer AOT-vänligt.
Verklig användbar upplevelse
Jag sätter igång min AOT,
- Digital Ocean Droplet (Linux x64) - funkar som en systemad tjänst
- Raspberry Pi 5 (Linux ARM
- Windows-Server (Windows x
- Dockercontainer - både x64 och ARMM SK2 varianter
Einsatzprocessen är identisk överallt:
- ladda ner arkiven från GitHub Releases
- Utvinna det
- kör den utförbara
Ingen versionskonflikter.
För Docker, min Dockerfile är pinsamt enkelt
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"]
Den resulterande bilden är ~130MB ( mestadels basen av Debian-bilden ). Ett traditionellt | | . | Net-container skulle vara |
Ett fungerande exempel
Allt jag har visat här kommer från ett riktigt projekt.
- Se källkod: Det mest liksägade.BotDetection.Konsole
- Se GitHub Actions arbetsflödet: konsol-utgångspunkt
- ladda ned förebyggna binär: GitHub-utsläpp
Projektet är en minimal YARP omvänd proxy med Bot-detection middleware som registrerar signer till SQLite. Det demonstrerar
- Infödda AOT med SQLite
- Multi-platform bygger via GitHub Actions
- Inhemska biblioteksbundling
- CLI argument解析
- Strukturerad skogning
- Ganska avstängning
Klona det , studera det, använda det som en template för dina egna AOT-projekt
resurser
- Officiell .NET Indiska AOT Dokumentation
- SQLitePCLRaw GitHub Repository
- Ett fungerande exempel : Det mestlucid Bot Detection Gateway
- Min GitHub Actions Workflow
Nu bygger du något litet och snabbt.