Si usted ha estado tratando de enviar aplicaciones .NET como ejecutables únicos y nativos usando AOT nativo, usted probablemente ha ejecutado cara-primero en la pared SQLite. Usted consigue todo configurado, la construcción se completa con éxito, y luego —boom— tiempo de ejecución se bloquea con críptico DllNotFoundException errores sobre e_sqlite3. Déjame ahorrarte las horas de frustración que pasé y mostrarte exactamente cómo hacer que SQLite trabaje con Native AOT a través de Windows, Linux (incluyendo ARM64 Raspberry Pi), y macOS.
Empecemos con los conceptos básicos absolutos. Si alguna vez has construido una aplicación .NET y te has preguntado por qué necesitas instalar el ".NET Runtime" en los servidores o por qué tu aplicación de consola tarda un segundo en empezar la primera vez que lo ejecutas, AOT es la respuesta a esos problemas.
Cuando escribes código C# y construyes tu aplicación, el compilador no produce código de máquina que tu CPU pueda ejecutar directamente. En su lugar, produce algo llamado Lengua intermedia (IL)—pensar en ello como a mitad de camino entre el código C# y las instrucciones reales de la máquina.
Cuando ejecutes tu aplicación .NET, esto es lo que sucede:
Esto es como tener un traductor que lee su receta (IL) y la traduce verbalmente a un chef (CPU) línea por línea mientras están cocinando. Funciona, pero hay arriba:
Native AOT voltea este modelo en su cabeza. En lugar de traducir su código en tiempo de ejecución, se traduce todo en el tiempo de construcción. Terminas con un único archivo ejecutable que contiene código de máquina real que tu CPU puede ejecutar directamente: sin tiempo de ejecución, sin traductor, sin espera.
Piense en ello como conseguir un libro de recetas traducido profesionalmente en lugar de contratar a un traductor en vivo. El trabajo se hace una vez, por adelantado, y el resultado está listo para usar inmediatamente.
Esto es lo que el AOT nativo te da:
1. Pequeños ejecutables: 10-30MB en lugar de 150MB+
Su aplicación y todo lo que necesita se compila en un pequeño binario. No hay archivos de tiempo de ejecución separados.
2. Inicio instantáneo: 80% más rápido comienza el frío
Mis pruebas: normal .NET tomó ~800ms para comenzar, AOT tomó ~150ms. No hay tiempo de calentamiento JIT — el código de la máquina está listo para ejecutar inmediatamente.
3. Cero dependencias: No se requiere tiempo de funcionamiento .NET
Puede copiar su ejecutable a cualquier máquina con el sistema operativo correcto (Windows/Linux/Mac) y simplemente se ejecuta. No hay requisito previo para "instalar .NET 9 Runtime".
4. Menor uso de memoria: Alrededor del 50% menos de memoria
No hay compilador JIT sentado en la memoria. En mi aplicación de puerta de enlace, normal .NET utilizado 85MB inactivo, AOT utilizado 42MB.
5. Mejor para entornos restringidos: Funciona donde JIT no puede
Algunos entornos (algunos contenedores Docker, iOS, sistemas integrados) no permiten la generación de código de tiempo de ejecución. AOT funciona en todas partes.
AOT no es magia, estás intercambiando flexibilidad de tiempo de ejecución para la optimización inicial:
1. No hay generación de código dinámico
Cualquier cosa que genere código en tiempo de ejecución no funcionará:
System.Reflection.Emit (creando tipos dinámicamente)El código .NET más normal está bien, pero algunos marcos que dependen en gran medida de la reflexión necesitan una configuración especial.
2. Estructuras específicas de la plataforma
JIT compilation produce IL que se ejecuta en cualquier plataforma. AOT produce código de máquina nativo para una plataforma específica. Usted necesita construir por separado para:
Cubriremos automatizando esto con GitHub Actions más tarde.
3. Tiempos de construcción más largos
En lugar de compilar a IL en segundos, AOT compila todo el camino al código de la máquina. Espere 2-5 minutos en lugar de 10 segundos. Es un costo de una sola vez para los beneficios permanentes.
4. Algunas características necesitan configuración adicional
La serialización de JSON, Entity Framework y cualquier cosa que utilice reflexión pesada puede necesitarle que le diga explícitamente al compilador qué tipos guardar.
El AOT nativo es perfecto para:
Saltar AOT para:
Para herramientas CLI, microservicios, contenedores y dispositivos de borde, las mejoras están cambiando. Pero hay una captura cuando se agregan bases de datos, específicamente SQLite.
Ahora que usted entiende lo que es AOT, vamos a hablar sobre el punto de dolor más grande: SQLite. Esta es la pared que la mayoría de la gente golpea cuando intenta utilizar AOT con aplicaciones respaldadas por bases de datos.
Aquí está el típico viaje:
Microsoft.Data.Sqlite a su proyectoPublishAot=true en tu .csprojDllNotFoundException: Unable to load DLL 'e_sqlite3' or one of its dependencies
Tu aplicación se bloquea antes de que pueda hacer cualquier cosa. ¿Qué pasa?
Para entender el problema, usted necesita saber acerca de bibliotecas nativas.
SQLite no está escrito en .NET; está escrito en C. Está compilado en código nativo específico de la plataforma:
e_sqlite3.dll en Windowslibe_sqlite3.so en Linuxlibe_sqlite3.dylib en macOSCuando use Microsoft.Data.Sqlite en normal .NET, es sólo un envoltorio alrededor de esta biblioteca SQLite nativa. En tiempo de ejecución, trata de cargar dinámicamente el archivo nativo apropiado para su plataforma.
Esto funciona bien con .NET normal porque:
El AOT nativo tiene dos características que chocan con el enfoque de SQLite:
1. Recortes agresivos: AOT elimina cualquier código que crea que no está utilizando. Si no puede demostrar estáticamente que necesita algo, se elimina. Carga de biblioteca dinámica confunde el trimmer - no puede ver la conexión entre su código y el DLL SQLite nativo.
2. No hay soporte de carga dinámico: AOT produce un binario autónomo. Espera que todas las dependencias nativas se vinculen explícitamente en el tiempo de compilación, no se carguen dinámicamente en el tiempo de ejecución.
El resultado: Microsoft.Data.Sqlite espera encontrar una biblioteca SQLite nativa en tiempo de ejecución, pero AOT la ha recortado o no sabe cómo incluirla correctamente.
Peor aún, si tienes otros paquetes NuGet que usan SQLite (como algunas bibliotecas ORM o mi mostlylucid.ephemeral.complete paquete), puede terminar con múltiples proveedores SQLite incompatibles en tu árbol de dependencia.
Cada proveedor trata de trabajar de manera diferente:
El compilador AOT se confunde sobre cuál usar, y a menudo el resultado es que no incluye ninguno de ellos, o peor aún, incluye archivos conflictivos que no pueden trabajar juntos.
Después de horas de frustración, encontré la solución: SQLitePCLRaw.bundle_e_sqlite3. Este es un paquete especial de NuGet diseñado específicamente para trabajar con AOT.
Añada estos dos paquetes a su proyecto:
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.10" />
</ItemGroup>
Los bundle_e_sqlite3 el paquete es diferente de los proveedores SQLite normales:
1. Incluye bibliotecas SQLite nativas precompiladas para cada plataforma importante:
2. Estas bibliotecas nativas se empaquetan de una manera que el compilador AOT entiende
El paquete marca explícitamente sus dependencias nativas para que el compilador AOT sepa incluirlas en el binario final. No hay carga dinámica, no hay tiempo de ejecución en busca de archivos.
3. Está diseñado para construcciones multiplataforma
Un paquete funciona para todas las plataformas. No necesita paquetes específicos de la plataforma o referencias condicionales.
¿Recuerdas los dos problemas que identificamos?
Problema 1: El recorte de AOT elimina bibliotecas que no puede ver siendo utilizadas
Problema 2: AOT no soporta carga dinámica
El resultado: cuando se construye para Windows x64, el paquete incluye e_sqlite3.dll. Cuando se construye para Linux ARM64, incluye el ARM64 libe_sqlite3.soTodo funciona.
Esta es la parte que sube el 90% de la gente tratando de usar SQLite con AOT, incluyéndome en mi primer intento.
Con .NET normal, el paquete SQLite se inicializa automáticamente a sí mismo la primera vez que utiliza SQLite. La magia sucede detrás de las escenas, no es necesario hacer nada.
Con AOT nativo, esta inicialización automática no funciona. El compilador AOT no puede ver el código de inicio automático (parece código no utilizado y se recorta), por lo que debe inicializar manualmente el paquete al inicio de su aplicación.
Aquí está la línea mágica que necesitas:
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() ¿Hacer?Este método le dice al paquete SQLite que:
Microsoft.Data.Sqlite y la biblioteca nativaSe llama "Batteries" porque es el paquete de "baterías incluidas", todo lo que necesitas está empaquetado.
Poner Batteries.Init() como la primera línea en tu Main método, antes de:
Piense en ello como conectar un dispositivo antes de intentar encenderlo. Si intenta utilizar SQLite antes de llamar Init(), todavía tendrás el DllNotFoundException a pesar de que el DLL está correctamente incluido en su aplicación.
Si te olvidas de llamar Batteries.Init(), tu aplicación:
DllNotFoundException: Unable to load DLL 'e_sqlite3' or one of its dependencies
Esto es confuso porque el DLL es incluido en su solicitud — simplemente no está inicializado. Perdí dos horas por este error. No seas como yo.
Aquí hay un lleno .csproj configurado para AOT nativo multiplataforma 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>
Permítanme explicar lo que cada uno de estos ajustes hace:
PublishAot=true
Este es el switch maestro que permite la compilación Native AOT. Sin esto, se obtiene un comportamiento .NET normal (compilación JIT). Con esto, se obtiene con anticipación el código nativo compilado.
PublishTrimmed=true y TrimMode=full
Estos le dicen al compilador que elimine cualquier código que no esté usando. Piense en ello como limpiar su garaje antes de moverse — ¿por qué empacar cosas que no necesita?
PublishTrimmed=true permite recortarTrimMode=full significa "ser agresivo, quitar todo lo que puedas"Aviso: Esto puede romper el código que utiliza la reflexión pesada (como algunos seriadores JSON o ORMs) porque el recortador no siempre puede ver lo que estás usando a través de la reflexión.
InvariantGlobalization=true
Esto elimina todos los datos específicos de la cultura de su aplicación: formatos de fecha, símbolos de moneda, reglas de clasificación de texto para diferentes idiomas.
Sólo establecer esto a true si tu aplicación:
Si está construyendo una herramienta CLI o una pasarela API que no le importa la localización, esto es un ahorro gratuito. Si está construyendo algo que necesita formatear fechas para los usuarios franceses o ordenar correctamente el texto turco, omita esta configuración.
PublishSingleFile=true
Agrupa todo en un archivo ejecutable. En lugar de tener:
myapp.exe
myapp.dll
System.Text.Json.dll
... 50 more files
Usted consigue justo:
myapp.exe
Mucho más fácil de desplegar.
StripSymbols=true
Los símbolos de depuración ayudan a los depuradores a mostrar nombres variables y números de línea cuando depuran. Son útiles durante el desarrollo, pero añaden varios megabytes a su binario final.
Esta configuración los elimina. Tu aplicación se ejecuta exactamente igual, sólo que más pequeño.
OptimizationPreference=Speed
Esto le dice al compilador qué priorizar al tomar decisiones:
Speed: Hacerlo rápido (binarios ligeramente más grandes, pero mejor rendimiento)Size: Hacerlo pequeño (levemente más lento, pero tamaño binario mínimo)Para la mayoría de las aplicaciones, Speed La diferencia de tamaño es generalmente sólo 2-3MB, pero la diferencia de rendimiento puede ser notable.
RuntimeIdentifiers
Esto declara qué plataformas quieres apoyar. No las construye todas — simplemente le dice a las herramientas "estos son objetivos válidos".
Identificadores disponibles:
win-x64: Windows 64-bit (Intel/AMD)win-arm64: Windows ARM64 (Surface Pro X, etc.)linux-x64: Linux 64-bit (Ubuntu, Debian, RHEL, etc.)linux-arm64: Linux ARM64 (Raspberry Pi 4/5, AWS Graviton)osx-x64: macOS Intel (macs antiguos)osx-arm64: macOS Apple Silicon (M1/M2/M3 Macs)Construye una plataforma a la vez usando dotnet publish -r linux-x64, por ejemplo.
¿Recuerdas cómo dije que AOT requiere construcciones específicas de la plataforma? Necesitas compilar por separado para Windows, Linux x64, Linux ARM64, macOS Intel y macOS Apple Silicon. Hacer esto manualmente sería tedioso, pero podemos automatizarlo.
Si no estás familiarizado, GitHub Actions es un servicio gratuito CI/CD (Integración continua/Desplegamiento continuo) integrado en GitHub. Te permite ejecutar tareas automatizadas cada vez que presionas código o creas una etiqueta de liberación.
Piensa en ello como tener un servidor de compilación que:
v1.0.0Todo esto se ejecuta en los servidores de GitHub, no es necesario mantener ninguna infraestructura. Para proyectos de código abierto y pequeños proyectos personales, es completamente gratuito.
Aquí está un flujo de trabajo GitHub Actions que se construye automáticamente para todas las plataformas principales:
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
Si el YAML parece intimidante, esto es lo que hace en inglés:
1. Activador (on: push: tags)
El flujo de trabajo se ejecuta cuando pulsas una etiqueta Git que comienza con v (como v1.0.0, v2.3.1Esta es la forma estándar de marcar las versiones de lanzamiento.
2. Estrategia Matriz
Esta es la parte inteligente. En lugar de escribir cinco flujos de trabajo separados, definimos un matriz de construcciones:
os (máquina de correr) y runtime (plataforma objetivo)Así que cuando empujas v1.0.0, GitHub simultáneamente:
3. Pasos en cada edificio
Cada construcción de plataforma hace los mismos pasos:
dotnet publish con banderas AOT para esa plataforma específica4. El resultado
Después de completar todas las construcciones, tiene cinco artefactos (binarios) listos para distribuir. Puede descargarlos de la ejecución Acciones, o utilizar un segundo trabajo para crear una versión GitHub automáticamente (no se muestra en este fragmento, pero es fácil de añadir).
Aquí hay algo que me confundió durante horas: PublishSingleFile=true sólo paquetes .NET códigoBibliotecas nativas como SQLite's e_sqlite3.dll Mantente separado.
Es por eso que el paso "Crear paquete de distribución" es tan 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
Los 2>/dev/null || true part significa "si no hay archivos que coincidan con este patrón, no fail—justo continúe". Esto permite que el mismo script funcione en todas las plataformas.
Lo que usted distribuye:
myapp.exe + e_sqlite3.dll (agrupados en un ZIP)myapp + libe_sqlite3.so (agrupados en un tar.gz)myapp + libe_sqlite3.dylib (agrupados en un tar.gz)Los usuarios extraen el archivo y ejecutan el ejecutable. La biblioteca SQLite nativa se encuentra junto al ejecutable, y el paquete lo encuentra automáticamente en tiempo de ejecución.
Nótese que workflow_dispatch desencadenar en la parte superior:
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: 'Version to publish'
required: true
Esto le permite activar manualmente las construcciones desde la interfaz web de GitHub sin empujar una etiqueta. Útil para probar el flujo de trabajo o crear versiones beta.
La compilación de Linux ARM64 requiere una atención especial. Necesita herramientas de compilación cruzada y debe especificar la correcta objcopy herramienta:
# 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
Sin la ObjCopyName parámetro, el linker falla con errores crípticos sobre formatos de archivo no reconocidos.
Esto es lo que he logrado con mi puerta de detección de bot basada en YARP de producción con middleware y SQLite loging. Este es un proyecto real que puede descarga desde GitHub.
Estos son los Tamaños de archivos reales de mis versiones de GitHub, no estimaciones teóricas:
Plataforma Ejecutable SQLite Native DLL Tamaño total del archivo |----------|-----------|-------------------|-------------------| Windows x64 9.2MB +1.7MB 10,9MB (ZIP) Linux x64 10,8MB +1,6MB 12,4 MB (tar.gz) Linux ARM64 9.9MB +1.5MB 11.4MB (tar.gz) macOS Intel 11.2MB +1.8MB 13,0MB (tar.gz) macOS ARM64 9.8MB +1.7MB 11,5 MB (tar.gz)
Compárese esto con los despliegues autónomos de .NET 9 en 130-150MB por plataformaEstamos hablando de un 10-12x reducción de tamaño.
El desglose:
Ambos archivos deben ser distribuidos juntos, pero todavía son dramáticamente más pequeños que las implementaciones tradicionales de .NET.
Inicio frío (primera solicitud servida) en un VPS de Linux modesto:
Esto importa enormemente para:
Memoria inactiva (corredor de la puerta, sin tráfico):
Bajo carga (1000 solicitudes/segundo):
La memoria inferior significa:
Batteries.Init()Este es el error #1. Incluso con el paquete correctamente referenciado, olvidando llamar SQLitePCL.Batteries.Init() en el mismo comienzo de su Main el método causará fallos en el tiempo de ejecución.
La solución:
public static void Main(string[] args)
{
SQLitePCL.Batteries.Init(); // FIRST LINE
// ... rest of your code
}
Esto es el error #2, y me dio dos veces. PublishSingleFile=true NO incluye DLLs SQLite nativos en su ejecutable. Debe distribuirlos junto con su ejecutable.
¿Qué sucede si olvida:
DllNotFoundExceptionLa solución:
Al empaquetar su liberación, siempre incluya:
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)
En tus scripts de implementación o acciones de GitHub:
# 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
Los usuarios deben extraer el archivo y ejecutar el ejecutable. La biblioteca nativa necesita estar en el mismo directorio.
winsqlite3 ProveedorEs posible que vea recomendaciones para usar SQLitePCLRaw.provider.winsqlite3 en Windows para usar el SQLite proporcionado por el sistema operativo. No. Sólo funciona en Windows, requiere inicialización manual, y rompe las construcciones multiplataforma. bundle_e_sqlite3.
Si está usando Entity Framework Core con SQLite, obtendrá advertencias de recorte (IL2026, IL3050). Estos son generalmente seguros de suprimir para el proveedor de SQLite de EF Core, pero prueba a fondo:
<PropertyGroup>
<NoWarn>$(NoWarn);IL2026;IL3050</NoWarn>
</PropertyGroup>
Mejor aún, considere usar Dapper o RAW ADO.NET con SQLite para aplicaciones AOT: son más amigables con AOT.
En Windows, Native AOT requiere el enlace MSVC de Visual Studio. Si construye fuera de un programa de comandos de desarrolladores, obtendrá errores sobre vswhere.exe. GitHub Actions maneja esto automáticamente, pero para las construcciones locales, use:
O inicialice el entorno en su script de compilación:
& "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\Launch-VsDevShell.ps1" -Arch amd64
dotnet publish -c Release -r win-x64
El AOT nativo es perfecto para:
Evitar el AOT para:
Si has leído todo este post y sólo quieres una lista de verificación para seguir, aquí tienes:
1. Añadir los paquetes:
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.10" />
2. Configure su .csproj en el caso de AOT:
<PublishAot>true</PublishAot>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>full</TrimMode>
<InvariantGlobalization>true</InvariantGlobalization>
<PublishSingleFile>true</PublishSingleFile>
<StripSymbols>true</StripSymbols>
<OptimizationPreference>Speed</OptimizationPreference>
3. Inicialice SQLite al inicio de su Main método:
SQLitePCL.Batteries.Init();
4. Construir para su plataforma de destino:
dotnet publish -c Release -r linux-x64 --self-contained
5. ¡Pruebe su binario—sólo debe ejecutarse sin dependencias!
Hacer que SQLite trabaje con el nativo AOT no es obvio, pero una vez que conozcas el encantamiento mágicoSQLitePCLRaw.bundle_e_sqlite3 + Batteries.Init()La rentabilidad es sustancial: pequeños binarios, puesta en marcha instantánea y la capacidad de desplegarse en cualquier plataforma sin dependencias de tiempo de ejecución.
Para cualquiera que construya herramientas CLI, gateways o aplicaciones de borde con .NET, Native AOT con SQLite es ahora una opción realista. El flujo de trabajo de GitHub Actions proporcionado aquí automatiza todo el proceso de compilación multiplataforma, solo etiqueta una versión y listo.
Si eres nuevo en AOT, inicia pequeño: convierte una herramienta o utilidad CLI simple primero. Ponte cómodo con el proceso de compilación, aprende qué advertencias esperar, y entiende las limitaciones. Una vez que tengas lo básico abajo, puedes abordar aplicaciones más complejas.
El ecosistema .NET es cada vez más amigable con AOT. La mayoría de las bibliotecas modernas trabajan fuera de la caja o tienen documentación clara sobre el soporte AOT. El futuro es nativo, y es más rápido de lo que crees.
Implemento mi puerta de entrada compilada por AOT a:
El proceso de despliegue es idéntico en todas partes:
No hay "instalar .NET Runtime" paso. No hay infierno de dependencia. No hay conflictos de versión. Sólo extraer y ejecutar.
Por Docker, mi Dockerfile es embarazosamente 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"]
La imagen resultante es ~130MB (principalmente la imagen base de Debian). Un contenedor .NET tradicional sería de 200-250MB.
Todo lo que he mostrado aquí viene de un proyecto real, listo para la producción.
El proyecto es un proxy reverso YARP mínimo con middleware de detección de bots que registra firmas a SQLite. Demuestra:
Clonarlo, estudiarlo, utilizarlo como una plantilla para sus propios proyectos AOT.
Ahora ve a construir algo pequeño y rápido.
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.