# 与 SQLite @: @ how to get it working\ !}多平台 AOT 與 SQlite@MSK0. 如何讓它工作@ msk1 @

<!--category -- .NET, AOT, SQLite, DevOps -->
<datetime class="hidden">2025-12-16T15:00</datetime>

# 一. 导言导言 导言 导言 导言

本地端 AOT 承诺将您的“ . @ net ” 应用程序转换为小@ , @ self @ MS K2} 附加的可执行程序, 即刻启动并运行到任何地方@ — no runtime 安裝需要@ I. @ It'} 当它有效时魔法 @ MPK6} 但是在混合中添加 SQLite , 然后突然您@'\ re debugcipic `DllNotFoundException` 与中转依赖者战斗 , `PublishSingleFile=true` 这是完整的 SQLite 与本地人 AOT,#,+ @和GitHub Action=.+}自动释放专家指南。 到最后为止,您已经拥有了“10-12+MB”二进制在 Windows@,_Linux @(+包括 Raspberry PiMPK13+和无依赖性 MacOSMS14+.+#

## 什么是AOT? NSK1}和为什么你应该关心 ?)

Let“'”%s 以绝对基本数@' @AOT为起始点, 如果您在服务器上建立过“ .+NET” 应用程序并想知道为什么您需要在服务器上安装“ I".QNET Runtime @"}或者为何您的控制台程序第一次运行时需要一秒钟才能启动 “ MS K6_ AOT” 是这些问题的答案吗?‘.’ *

### @.@NET Ormal Working 如何正常工作 @%(#JIT汇编_)

当您写入 C#}{} 代码并构建您的应用程序 ,编译器 does '%t 生成机器代码, 您的 CPU 可以直接执行 .} 取代,} 它可以产生一种叫做 **中间语言 @(L)**@—_ 把它想成是您的 C#}代码与实际机器指令之间的一半

当您运行您的“ .@NET app,” 时这里 @ MS K2 @s what happens @ MPK3}

1. 您的计算机加载了 {.}NET 运行时间 @(} a separate program *\ )} {}
2. 运行时间读取您的 IL 代码
3. **JJIT)** 编译器将您的应用程序运行时的 IL 转换为本地机码
4. 你的CPU终于执行机器代码了

这就像有一位翻译 读您的食谱“(L)” 并用口头翻译给厨师 “(CPU)线条”, 而他们”'re cookn}".它管用的"MS K6,但是"但"'s abountMSKO8"

- @ .NET 运行时间是@ 50-150MB 您需要部署的额外文件
- JIT 编译器需要时间翻译代码 @ (# 为什么您的应用程序第一次变慢@) @%
- JIT 编译器本身在您的应用程序运行时处于记忆中

### 输入 AOT <(\ ahead> - OOF}-}时间编译_)

AOT 原生自动翻转这个模型的首字母“.” 而不是在运行时翻译您的代码@,}它翻译了一切 **建设时间**. {% } 您最后有一个包含您CPU可以直接运行的实际机器代码的可执行文件@ MS K1 @ no runtime ,} 没有翻译@ MPK3_ no waiting.}

工作一旦完成, 就会立即投入使用#.@

### 游戏=-}改变津贴

这里的' 土著AOT给了你什么 :

**1.细小的可执行文件**而不是"150\MB3"

您的应用程序及其所需要的一切, 都编译成一个小二进制@ . @ 没有单独的运行时间文件 @ MS K1} *

**MPK0 即时启动**快速的寒冷开始

我的测试“:”正常的@. @NET 采取了#~800_%s来启动‘,’AOT 抓走了$~150ms*.}那里没有JIT热度>-\QUP时间{—}机器代码准备立即执行=.}

**零依赖性**@: 不需要@.NET 运行时间

您可以将您的可执行文件复制到任何机器中, 正确的 OS @ ( @ Windows@ MS K1 @ Linux @ MPK2 @ Mac)} 它只运行“ .” 没有 @ I"安裝 @ MCK6 @ NET @ OMK7_ Runtime+"# minutes @ . @ *

**4. 记忆用量减少**@:}关于#50%∮ 少一些内存

在我的网关应用程序@,}正常的 @.NET使用“85MB 闲置”%,AOT 使用“42{MB_.}”

**更好地适应受限制的环境**"M:"在JIT可以工作的地方工作

允许运行时间代码生成@._AOT在各地工作 @.}

### 《交易》总在追逐《交易》3

AOT是 't magic.你're 前期优化的运行时间灵活性=:

**1. 不生成动态代码**

任何在运行时生成代码的信息 won't work@:

- `System.Reflection.Emit` 创造型号动态的)
- 运行时装入 DLL的动态组装 *\ ( {
- 一些花哨的思考技巧

大多数正常的 .NET 代码是罚款\,}但是一些非常依赖反射的框架需要特殊配置\MS K2}

**2._平台 @-}特定建筑**

JIT编集产生在任何平台上运行的IL=.AOT 生成本地机器编码。 **一个特定平台**. 您需要为 @:另建

- Windows x @ 64
- Linux x64
- Linux ARMQ64 ( 草莓皮 I)
- MACOS 情报
- 苹果硅

以GitHub Actions为主,

***3. 更长的建筑时间***

而不是在几秒内编集到 IL@, @AOT 汇编到机器代码\ .}期待 @2-5#分钟 而不是“10_秒{\.} It'}它是一个永久收益的 -时间成本 *.*

**4. 某些特性需要额外配置**

JSON 序列@,}实体框架 @,}以及任何使用重反射的方法可能需要您明确告知编译者哪些类型可以保留\.}(We'}) 会覆盖这个#.}

### 当您使用 AOT ? 时

本地 AOT 完美地适合 @: @ @

- **CLI 工具**@ :_ 命令@ MS K1} 即时启动事件所在的线性公用设施
- **微观服务**在 Kubernetes 中快速缩放
- **无服务器@/ @Lambda**寒冷的开始时间直接影响到您的账单
- **边缘装置**“: 草莓皮 , 资源有限的IT设备”
- **网关应用程序**: 反向近身 MS K1 API 高流量网关
- **桌面工具**“: 一艘没有“"安插”的 .exe 号船首先“MS K4” 步

跳过 AOT 为":

- 启动时间的传统的网络应用程序@' @tmatter
- 使用大量迁移的重实体框架应用程序
- 动态装入 DLLS 的插件系统
- 运行时生成代码的任何信息

对于 CLI 工具 @,微服务@,容器 @MS K2和边端设备 @,, 改进是游戏_-changing{.} 但是如果添加数据库\', 当您加入数据库时会有一个渔获Q— 指定为 SQLite.}

## SQLite 问题

现在,你明白AOT是什么东西了吗? ,吗? 让'来谈论一个最大的疼痛点吗?

### 发生什么了?

这里的'是典型的旅程 :

1. 您添加 `Microsoft.Data.Sqlite` 对您的工程项目
2. 您配置 `PublishAot=true` 在您 `.csproj`
3. 构建完整无错@— @ 一切看起来都很好}#!
4. 您运行可执行文件, 并立即获得@ : @ {}

```
DllNotFoundException: Unable to load DLL 'e_sqlite3' or one of its dependencies
```

您的应用程序在它能做任何事情之前就崩溃了 @ . @ what gives@ MS K1 @

### 了解土著图书馆 @(_A 快速Detour})

要了解问题 您需要知道 **本地图书馆**.

SQLite is @' @ t 写为 @ .NET @ I—} MSC3} 以 C\.} It'} 编译成平台=-# 特定本地代码@:}

- `e_sqlite3.dll` 视窗
- `libe_sqlite3.so` 关于Linux的
- `libe_sqlite3.dylib` 在 macOS 上

使用时 `Microsoft.Data.Sqlite` 在“.@NET @,}它只是围绕这个本地 SQLite 库的包装纸_.}在运行时间,} 它尝试着 **动态自动装入** 您平台的适当本地文件@ . @

这与正常的 .NET 工作顺利,因为 @:

1. 运行时间可以动态装入库
2. NuGet 软件包可以包括所有平台的本地图书馆
3. 右方在运行时被选中

### 为什么AOT打破这个

原住民AOT有两个特点,与SQLite's方法相冲突

**1. 侵略性修剪**:AOT 移除任何它认为您没有使用过的代码 如果它能静态地证明 您需要的东西,} 它会被删除・.}动态库装填混淆了您的代码与本地 SQLite DLLZ. 的连接

**& 2.无动态装入支援**@:_AOT 產生自@-}足裝的二進位 *.} 它期望所有本地依存點在編譯時會被明确連結 {,沒有在運作時动态載入}.}

结果 #:\ {} `Microsoft.Data.Sqlite` 期待在运行时找到本地 SQLite 库@, @ but AOT 已将其剪切或不知道如何适当绑定@MS K2}

### 过渡依赖性噩梦

如果您有其他使用 SQLite {(} 的 NUGet 软件包, 像一些ORM 库或我的 `mostlylucid.ephemeral.complete` MSMK0} {} 您可以以 **多重不兼容 SQLite 供应商** 树上的相依值@. @%

每个供应商都尝试以不同的方式工作 :

- 人们可能会期待OS- 提供 SQLite * (* 窗口只@)* *
- 另一个可能把自己自己的SQLite捆绑起来
- 另一种可能使用不同的本地图书馆版本

AOT 编译器混淆了使用哪个@, @ 通常结果是它没有包含其中任何一个\—_ or werse{,} 包括互相冲突的文件, 它们能够“ 'Qt 一起工作 ” @.}

## 解决方案_:}使用套装

解决办法是 `SQLitePCLRaw.bundle_e_sqlite3`“—a 特殊Nuget软件包”,专门设计用于与AOT.

在您的工程中添加这两个软件包@ : @

```xml
<ItemGroup>
  <PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
  <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.10" />
</ItemGroup>
```

### 什么是捆绑 Do? ?

缩略 `bundle_e_sqlite3` 与普通 SQLite 提供者不同的软件包@: @% 1

**包括所有主要平台的本地SQLite图书馆@: @**

- 視窗 @ (_x64,} x86,}ARMEZK3
- Linux (XMS K1ARMK2 musl-以Alpine=)为基地的突变器
- @(_x}64}英特尔•,}ARM•MS K3}苹果硅 )

**@2.这些本地图书馆是用AOT汇编者理解的方式包装的。**

捆绑标记其本地属地, 让 AOT 编译者知道将它们包含在最终的二进制中@ . @ 没有动态装入@ I,} 没有运行时间搜索文件\ MS K2 *

**用于 cross @-}#platform building. {\fn方正黑体_GBK\fs18\b1\bord1\shad1\3cH2F2F3F}{\4cH000000}#3.It'**

一个套件适用于所有平台@ . 您不需要平台 @ MS K1} t需要平台\ -} 特定软件包或有条件的引用@.}

### 为何这样工作

记住我们发现的两个问题

**问题 @ 1:AOT'}s trigmming 删除它能够 ' t看到正在使用的库**

- 将本地图书馆明确宣布为建设必须包含的资产。

**问题 @ 2:} AOT does'}% t 支持动态装入**

- SQLite 库在编译时静态链接

当您为 Windows x 64,}构建窗口时,此包包含 `e_sqlite3.dll`当您为 Linux 建造 ARM @64, 时, 它包括 ARMESK2 * `libe_sqlite3.so`万事如意

### 启动 Bundle & M(&Don'+T 跳过此 <!) n% 1}

这里的'是人们尝试使用 SQLite 与 AOT}MSKK2 包括我第一次尝试时,

**使用正常的“.NET”**,SQLite捆绑在您第一次使用 SQLit@. Magic 在幕后发生时自动初始化自己 @—you donZMK3t needs to do anything@MS K4

**与原住民AOT**,}( 此自动初始化) @ MS K1_ t工作@ .} AOT 编译器 can'} 看不到自动启动代码@ I(} 它看起来像未使用的代码, 并获得修补@ MPK5} 所以您可以看到自动启动编码@ msk4 @ title **必须手动初始化** 您应用程序初始时的捆绑@ . @

这里您需要的魔力线是:

```csharp
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();
    }
}
```

### What do 是什么 `Batteries.Init()` 做?

此方法告诉 SQLite 捆绑到 @ : @ {}

1. 为当前平台查找正确的本地 SQLite 库
2. 装入内存
3. 将所有连接连接起来 `Microsoft.Data.Sqlite` 和本地图书馆

它叫做“"”和“Batteries " ” 因为它“'” 包括了“MS K4”和 “"”

### 将它放在哪里

将 `Batteries.Init()` 计为 **第一行第一行** 在您 `Main` 在 :\ {} @ ,\ 之前

- 创建应用程序构建器
- 减轻依赖性注射
- 打开任何数据库连接
- 读取可能使用 SQLite 的配置文件

如果您在拨打 SQLite 之前尝试使用SQLet, 请先把它想象成插入设备前的插件 `Init()`{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}你 `DllNotFoundException` 即使 DLL 在您的应用程序中被正确捆绑@ . @%

### 如果你忘记了?会怎样?

如果你忘了打电话 `Batteries.Init()`“,”你的应用程序将“:”

1. 成功构建 & ( @ the 编译者 won\ ' @ t 警告您@ MS K2 @
2. 开始运行
3. 当它试图使用 SQLite 时按下“ : ” (% 1)

```
DllNotFoundException: Unable to load DLL 'e_sqlite3' or one of its dependencies
```

这令人困惑,因为 DLL **是** 尚未初始化@. @%

## 完成工程配置

这里的' 满 `.csproj` 已配置为多@ - @platform 原生 AOT , 使用 SQLite@:}

```xml
<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>
```

### 解析密钥设置@( @ in Plain English_)}

让我来解析一下这些设置中 每一个设置都做了些什么

**`PublishAot=true`**

这是允许本地人 AOT 编译的主开关@. @ 没有这个“ , @ ” 您就会得到正常的 “ . @ NET 行为” @ MS K3 @ JIT 编集 @ I).Q} 您可以用这"\ ," #-of @-_time 已编译原源代码=. @ @

**`PublishTrimmed=true` 和 `TrimMode=full`**

这些人告诉编译者要删除任何您没有使用的代码@' @re @.}在移动%—#之前,想一想清扫车库吗?

- `PublishTrimmed=true` 启用修剪功能
- `TrimMode=full` 意指“"abe abusive”\",}删除所有你能读到的"MS K2"

**警告警告警告**这可以打破使用重反射 (的代码,就像一些 Json 序列器或ORMSQ)的代码 因为三进制能够...

**`InvariantGlobalization=true`**

这将从您的 App—}日期格式 ,}货币符号@,}文本排序规则中删除所有用于不同语言的 culture {-} 特定数据=%5-10MB_.}

仅设定此位置为 `true` 如果您的应用程序@ : @ @ label

- 只使用英语
- 'T 需要文化@-}指定日期 @/时间格式化
- 仅使用 ordinal @ ( @ byte@ MS K1_ by% -\ byTE @ MPK3}字符串比较

如果您在建立 CLI 工具或 API 网关时不关心本地化 @, 这是免费储蓄@. 如果您在为法国用户创建了需要格式化日期的东西, 或者对土耳其文本进行校正@ ,跳过此设置\.

**`PublishSingleFile=true`**

将所有内容捆绑到一个可执行文件@ . @ 而不是有\ MS K1} @

```
myapp.exe
myapp.dll
System.Text.Json.dll
... 50 more files
```

你得到的只是 :

```
myapp.exe
```

部署更加容易

**`StripSymbols=true`**

调试符号帮助调试器显示您在调试@ . @ they're 在开发过程中有用, 但会在您的最后一个二进制中添加数兆字节@ MPK2} *

此设置删除它们@ . 您的应用程序运行完全相同 , 仅小一点 @ MS K2

**`OptimizationPreference=Speed`**

这将告诉汇编者在做决定时要优先考虑什么@: @%

- `Speed`@: @ 让它快速 @ ( @ 稍大一点的二进制@ MS K2 @ 但表现更好 @ MPK3 @
- `Size`@ :_ 让它小一点@ MS K1} 稍慢点 @ MPK2} 但最小的二进制大小 @ IMK3

对于大多数应用程序来说 @, `Speed` 大小差异通常只有 @2-3}{MB},},但性能差异是明显可见的>.

**`RuntimeIdentifiers`**

此声明您想要支持的平台@ . @ it does\ ' @ t 构建它们全部@ I— @ it just telling toolking k3}这些是有效的目标 @ MS K4}

可用的标识符@: @%

- `win-x64`:+Windows @64-_Bit@(#Intel}/=AMD+)
- `win-arm64`:+WindowsARMK1+(+Surface pro X,等#.)+@
- `linux-x64`@( @Ubuntu@,Debian},RHEL#,等等 .)
- `linux-arm64`:Linux ARM64 MS K2Raspberry Pi _4/5,AWS Graviton)
- `osx-x64`-=YTET -伊甸园字幕组=- 翻译:
- `osx-arm64`:+MACOS苹果硅

你一次用一个平台构建 `dotnet publish -r linux-x64`以,为例,

## 多平台大楼

记得我说过AOT需要平台=-}特定建筑_?}你需要为 Windows,\}Linux x64,}LINux ARMZK4}MAcOS Intel@,}和 Macos Apple Silicon}.}做这个手动操作会很无聊吗? —}但是我们可以把它自动化

### GitHub Action?是什麽?

如果您在 GitHub. 中建起的“ '” 服务, 如果您不熟悉@ MS K1 @ GitHub Action is a free CI/QQCD @ (} “ 持续整合” @ I/# 连续部署@ MPK5 服务 。 它允许您在按下代码或创建解码标签时运行自动任务@.}

把它想象成拥有一个建筑服务器

1. 观察您的 GitHub 仓库
2. 当您按下标签时 `v1.0.0`
3. 它自动旋转 Windows @ ,Linux,和 MacOS 虚拟机器
4. 平行构建所有平台的应用程序
5. 创建 GitHub 版本,并附加所有二进制文件

所有这些运行在 GitHub' @services@—}您不需要维护任何基础设施@ .} 对于开放的-{来源项目和小型私人项目_,}它#'}完全免费>.}

### 完整的建设工作流程

自动为所有主要平台构建的 GitHub Action 工作流程@: @%

```yaml
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
```

### 此工作流程如何工作 : @ ( @ step by Step_ )}

如果YAML看起来吓人的话  ,

**答题机: 1.`on: push: tags`)**

当您按以 Git 标记开头时,工作流程会运行 `v` 类似 @( `v1.0.0`, `v2.3.1`这是标注发布版本的标准方式 @.@

**2.矩阵战略**

这是聪明的部份 @ . @ @ 而不是写入五个不同的工作流程@ MS K1 @ 我们定义了一个 **矩阵总表** 建筑物的建造情况*: *

- 每栋建筑都有不同的 `os` -=YTET -伊甸园字幕组=- 翻译: `runtime` @(#目标平台_)}%
- GitHub Action 运行所有五个建筑 **平行并行**
- 每一件都产生一件工艺品 “(”“编译的二进制书”“)”

所以当你推 `v1.0.0`GitHub同时 :

- 旋转一个 Ubuntu 机器以建造 Linux x64和ARM @ 64
- 将用于构建 Windows x 64\ 的 WindwC 机进行回旋
- 旋转一个 MacOS 机器以制造macos x64和ARM\□64

**每一个建筑中的阶梯**

每个平台的建设都采取同样的步骤 :

1. **退出代码码**: 从仓库获取您的源代码
2. **设置“.NET”**@:_安裝@._NET {10}%SDK
3. **安装 RAR@ 64 @% 工具** (Linux ArM64 仅{):}安裝 cross-}編譯工具
4. **出版**: 运行 `dotnet publish` 该特定平台的 AOT 旗帜
5. **上传工艺品**: 保存已编译的二进制, 这样下一个工作可以访问它

***4.* 结果**

全部构建完成@, @% 您有五件工艺品 @ ( @ binaris @ MPK2} 准备发行 .} 您可以从 Action 运行中下载它们 MS K4, 或者使用第二个工作来创建 GitHub 释放 自动显示 #( @ 未在此片段显示, 但很容易添加\ I). {

### 土著图书馆没有被淹没

这里的' 的东西 让我迷惑了几个小时 **`PublishSingleFile=true` 只捆绑 @ .@NET 代码**SQLite 's等土著图书馆 `e_sqlite3.dll` 单独停留@. @%

这就是为什么 @"_Create 分配宏包@"}步如此重要的原因

```bash
# 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
```

缩略 `2>/dev/null || true` 如果没有匹配此模式的文件 @ , @ do'_ t失败@ MS K3} 只继续\ ."{ 这让所有平台的脚本工作都相同 @ MPK5}

**您分发的@ : @ {}**

- Windows : `myapp.exe` + `e_sqlite3.dll` @( 混合在一起 在ZIP)
- Linux : `myapp` + `libe_sqlite3.so` @( 混在一块塔里 #MSSK1#gz)#
- 马科斯马库斯州 `myapp` + `libe_sqlite3.dylib` @( 混在一块塔里 #MSSK1#gz)#

用户提取归档并运行可执行文件@. 本地 SQLite 库位于可执行文件中的旁边 @,, 捆包在运行时自动发现它@ .

### 为什么手动触发手册有用

通知 `workflow_dispatch` 在顶端的触发点@ : @

```yaml
on:
  push:
    tags:
      - 'v*'
  workflow_dispatch:
    inputs:
      version:
        description: 'Version to publish'
        required: true
```

这样您就可以从 GitHub's 网络界面手动启动触发程序, 而不用按标签@ .# 用于测试工作流程或创建 beta buildingZ.}

### ARM64十字MS K1集

Linux ARM @64}构建需要特殊注意 您需要交叉@-\% 编译工具, 必须指定正确的 `objcopy` 工具“:”

```bash
# 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
```

没有 `ObjCopyName` 参数@, @% linker 失败, 在不被认可的文件格式上出现加密错误@MS K1}

## 真实的-世界成果

我制作的YARP-}以机器人检测网关为基地,用中器和 SQLite secripation_.}这才是真正的项目 [从 GitHub 下载](https://github.com/scottgal/mostlylucid.nugetpackages/tree/main/Mostlylucid.BotDetection.Console).

### 二进制大小 {(}\ {来自实际的 GitHub 释放= )}

这些是 **实际文件大小** 而非理论估计@:

|平台 @|可执行@|SQLite本地 DLL @MS K3总存档大小 @|
|----------|-----------|-------------------|-------------------|
|+Windows x64+|} @9.2+MB #|+#+1.7#MB#MS K6# **{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}万岁** (IP) □|
-=YTET -伊甸园字幕组=- 翻译: **{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}万岁** (tar.gzMS K2 MSC3
|Linux ARM64 MS K2 I9.9 MB| NSK5MBMPK6 **{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}万岁** (tar.gzMS K2 MSC3
| MACOS Intel| MS K2 MB| NSK4MB □| **{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}万岁** (tar.gzMS K2 MSC3
|+MACOSARMK1+I|+ MS K3+MB+|+NSK5=MB#|_ **{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}万岁** (tar.gzMS K2 MSC3

与自@-}自定义的 @ .NET @10部署相比 **每个平台的130-150MB**我们在谈论一个 **10-12x 缩小体积**.

明细表=:

- 主可执行程序 @ : 您编译的代码@ MS K1 @ I.NET 运行时间 @ MPK3AOT}-compiled\ )
- 本地 DLL: SQLite 数据库引擎 @ ( 写在 CQ)@

两个文件必须一起分布\,}但是它们仍然远远小于传统的\.\NET部署@.}

### 启动启动性绩效

寒冷开始@( @ 第一次请求已送达 @ ) @ 在一个温和的 Linux VPS:}

- **自定义@ - @ 内装的 @ MS K1 @ net***: ~800*
- **土著AOT***: ~150*
- **改进**动作快一点

这对:来说 事关重大

- 无服务器=/Lambda 函数 @(}您支付每毫秒@)
- 经常缩放的容器@/
- CPLI 工具,其中每个发号施令都重新开始

### 内存使用

无流量=):

- **自定义@ - @ 内装的 @ MS K1 @ net**{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}万岁!
- **土著AOT**{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}万岁!
- **改进**减量

在“(1000 ” 装载下请求@/ @second @):

- **自定义@ - @ 内装的 @ MS K1 @ net**{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}万岁!
- **土著AOT**{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}万岁!
- **改进**减量

下层内存意指:

- 每个东道主更多集装箱
- 廉价云收帐
- 资源可行性@-_限制装置 @(_Raspberry Pi,}IoT#){

## 共同点

### 忘却 `Batteries.Init()`

这是“#1”错误“% . ” 即使包正确引用了“ MS K2” , 忘了拨打 `SQLitePCL.Batteries.Init()` 在您开始的时候 `Main` 导致运行时崩溃的方法@. @%

**修正@: @%**

```csharp
public static void Main(string[] args)
{
    SQLitePCL.Batteries.Init(); // FIRST LINE
    // ... rest of your code
}
```

### “2.”不传播土著图书馆

这是错的#2, 它给了我两次"MSC1" `PublishSingleFile=true` 不将本地 SQLite DLLs 捆绑到您的可执行文件中@ . @ 您必须将其与可执行文件一同分发@ I.+%

**万一你忘了怎么办?**

- 你的应用程序构建精细
- 它运行在您的 dev 机器上很好 @ ( @ where SQLite could be already installed sQLit 可能已经安装了@)}%
- 它在生产时碰撞 `DllNotFoundException`

**修正@: @%**

当您的解密时\ , 总是包含\ I:\ {}

```
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)
```

在您的 GitHub 动作或部署脚本中@:

```bash
# 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
```

用户应该提取归档并运行可执行文件@.}本地库需要在同一目录中 @.

### 使用“3.” `winsqlite3` 提供者

您可能会看到建议使用 `SQLitePCLRaw.provider.winsqlite3` 在 Windows 上使用 SOS @-}提供 SQLite@.}唐'\t . 的窗口 `bundle_e_sqlite3`.

### 4.Trim警告,带有EF核心

如果您使用 SQLite , 的实体框架核心, 您将获得修饰警告 : @(IL _2026, IL3050).}这些通常对 EF Core{'}SQLit provides@,}来说是安全的。 但是要彻底测试\:}

```xml
<PropertyGroup>
  <NoWarn>$(NoWarn);IL2026;IL3050</NoWarn>
</PropertyGroup>
```

最好考虑使用 Dapper 或原始 ADOQ.NET 和 SQLite 用于 AOT 应用程序@— @ they@MS K3#re more

### 缺少视觉演播室工具@( @窗口 @)#

在 Windows 上“,” 本地 AOT 需要视觉工作室@' @%s MSVC linker @.}如果您在开发者指令外建构, 提示\,你#'}你会得到错误 `vswhere.exe`@ ._ GitHub Action 自动处理此操作@ MS K1, 但本地建材@ MPK2}使用@ I:{

- VS {2022} 的开发者命令提示@ info
- VS 的开发者 PowerShell @ 2022 @%

在您的构建脚本中初始化环境@ : @

```powershell
& "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\Launch-VsDevShell.ps1" -Arch amd64
dotnet publish -c Release -r win-x64
```

## 何时使用本地 AOT

本地 AOT 完美地适合 @: @ @

- **CLI 工具**MPK0 即时启动事项
- **按摩服务和容器**: 小型图像@, 快速缩放
- **边缘装置**“: 草莓皮 , 资源有限的IT设备”
- **无服务器@/ @Lambda**寒冷的开始时间是关键
- **Gateoffer /}代理应用程序***:高通量 ,低费*

避免 AOT @: @%

- 重实体框架核心使用@(%s complicate questions @)#
- 反射@-}重度应用
- 动态插件系统
- 需要运行时间代码生成的应用

## 快速启动检查列表

如果您读过这整篇文章, 只想要一份核对表来跟随 @,}这里您去查询@:

**@1. 添加软件包@:**

```xml
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.10" />
```

**@2. 配置您的 `.csproj` 为AOT:**

```xml
<PublishAot>true</PublishAot>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>full</TrimMode>
<InvariantGlobalization>true</InvariantGlobalization>
<PublishSingleFile>true</PublishSingleFile>
<StripSymbols>true</StripSymbols>
<OptimizationPreference>Speed</OptimizationPreference>
```

**3. 初始化 SQLite 在您开始的时候 `Main` 方法@:**

```csharp
SQLitePCL.Batteries.Init();
```

**@4. 构建您的目标平台+:#**

```bash
dotnet publish -c Release -r linux-x64 --self-contained
```

**5. 测试您的二进制@— 它应该运行时没有依赖关系 @!**

## 结论结论

与原住民AOT合作的SQLite是“'@t obvious @,}但一旦你了解魔法咒语”=—`SQLitePCLRaw.bundle_e_sqlite3` + `Batteries.Init()`“— ' 直截了当”“. 回报是巨大的 @: 小二进制 I, 即时启动@, 以及在没有运行依赖性的情况下在任何平台上部署的能力$.

对于使用 SQLite 建立 CLI 工具“,” 网关@,}(或边端应用程序)的人来说, 用 @.NET,}用 SQlite 的本地 AOT 现在是一个现实的选项\.}GitHub 动作流程在此提供自动化了整个多Q-platform 构建进程@MS K6只是标出一个发布和您+'{re done#.}

如果您是 AOT,}新加入的 A' @ 开始小@ : @ 转换一个简单的 CLI 工具或工具 first @ MS K3{ 适应构建过程 *,# 学习什么警告可以期待\ ,} 并理解限制 < .} 一旦您把基本程序调低了",} 您可以处理更复杂的应用程序.}

多数现代图书馆要么在盒子外工作, 要么有关于AOT支持的清晰文件 '."未来是原创',\" 而它也比你想的快

## 实际部署经验

我调用我的AOT- 连接到MESK1的通道

- **数字海洋流** (Linux x64)}MS K2 以系统服务方式运行
- **草莓Pi5** @(LinuxARM64) -边缘部署测试
- **Windows 服务器** (Windows x @64)}- 以視窗服務執行
- **码头集装箱** - 和ARM64变量

部署过程无处不相同

1. 从 GitHub 版本下载归档
2. 抽取出来
3. 运行可执行文件

没有“"”安插@. @NET runtime @"}一步.}没有依赖性地狱{.}无冲突版本_.}只是提取和运行\.

给多克的 `Dockerfile` 是尴尬的简单@: @

```dockerfile
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"]
```

生成图像为 **{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}万岁** @(@ mostly the base Debian image_).}传统的 .NET容器将会是 @MS K3{MB}.}

## 完整工作示例

这里所显示的一切 都来自真实的, -Producation@-

- **查看源代码**: [Mostlucid.Bot 检测@.Console](https://github.com/scottgal/mostlylucid.nugetpackages/tree/main/Mostlylucid.BotDetection.Console)
- **见 GitHub Action 工作流程**: [控制台 - @gateway@-_release}.yml](https://github.com/scottgal/mostlylucid.nugetpackages/blob/main/.github/workflows/console-gateway-release.yml)
- **下载预建的二进制**: [GitHub 释放](https://github.com/scottgal/mostlylucid.nugetpackages/releases)

此工程是一个最小的 YARP 反向代理, 带有机器人检测中器, 记录 SQLite. 的签名 。 它演示了 @ : {}

- 使用 SQLite 的原生 AOT
- 由 GitHub Action 构建的多-平台格式
- 土著图书馆捆绑
- CLI 论点解析
- 结构性伐木
- 优利关闭

使用它作为您自己的 AOT 项目的模板@. @

## 资源资源资源 资源和资源资源资源

- [官方的“.NET土著AOT文件”](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/)
- [SQLitePCPRRaw 吉特Hub仓库](https://github.com/ericsink/SQLitePCL.raw)
- [工作示例@: Mostlylucid Bot 检测网关](https://github.com/scottgal/mostlylucid.nugetpackages/tree/main/Mostlylucid.BotDetection.Console)
- [My GitHub 行动工作流程](https://github.com/scottgal/mostlylucid.nugetpackages/blob/main/.github/workflows/console-gateway-release.yml)

现在去制造一些小东西,快一点 .