共用方式為


NativeAOT 支援和先行編譯查詢 (實驗性)

警告

NativeAOT 和查詢先行編譯是高度實驗性的功能,而且還不適合生產環境使用。 以下所述的支援應視為基礎結構,以使用 EF 10 發行最終功能。 建議您試驗目前的支援,並報告您的體驗,但建議您在生產環境中部署 EF NativeAOT 應用程式。 如需特定已知限制,請參閱下文。

.NET NativeAOT 允許發佈已預先編譯的自封 .NET 應用程式(AOT)。 這樣做提供下列優點:

  • 應用程式啟動時間大幅加快
  • 具有較小記憶體使用量且更容易部署的小型獨立二進位檔
  • 在不支援 Just-In-Time 編譯的環境中執行應用程式

使用 NativeAOT 發佈的 EF 應用程式啟動速度比沒有相同應用程式快得多。 除了 NativeAOT 提供的一般 .NET 啟動改進功能(亦即每次不需要 JIT 編譯),EF 也會在發佈應用程式時先行編譯 LINQ 查詢,以便在啟動時不需要處理,而且 SQL 已可供立即執行。 應用程式在其程式代碼中查詢 EF LINQ 越多,啟動取得的速度就越快。

發佈EF NativeAOT 應用程式

首先,為您的項目啟用 NativeAOT 發佈,如下所示:

<PropertyGroup>
    <PublishAot>true</PublishAot>
</PropertyGroup>

EF 在 NativeAOT 下對 LINQ 查詢執行的支持依賴 查詢先行編譯:此機制會靜態識別 EF LINQ 查詢併產生 C# 攔截器,其中包含執行每個特定查詢的程式代碼。 這可能會大幅減少應用程式的啟動時間,因為每次應用程式啟動時,處理和編譯 LINQ 查詢到 SQL 的繁重工作就不再發生。 相反地,每個查詢的攔截器都包含該查詢完成的 SQL,以及優化程式代碼,以將資料庫結果具體化為 .NET 物件。

C# 攔截器目前是實驗性功能,需要在專案檔中特別選擇加入:

<PropertyGroup>
  <InterceptorsNamespaces>$(InterceptorsPreviewNamespaces);Microsoft.EntityFrameworkCore.GeneratedInterceptors</InterceptorsNamespaces>
</PropertyGroup>

最後,套件包含 MSBuild 整合Microsoft.EntityFrameworkCore.Tasks會在發佈應用程式時執行查詢先行編譯(併產生必要的編譯模型):

<ItemGroup>
  <PackageReference Include="Microsoft.EntityFrameworkCore.Tasks" Version="9.0.0">
    <PrivateAssets>all</PrivateAssets>
    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
  </PackageReference>
</ItemGroup>

您現在已準備好發布 EF NativeAOT 應用程式:

dotnet publish -r linux-arm64 -c Release

這會顯示針對在ARM64上執行的Linux發行NativeAOT發佈; 請參閱此目錄 以尋找您的運行時間識別碼。 如果您想要在不發佈的情況下產生攔截器,例如檢查產生的來源,您可以透過 dotnet ef dbcontext optimize --precompile-queries --nativeaot 命令執行此動作。

由於 C# 攔截器的運作方式,應用程式來源中的任何變更會使它們失效,而且需要重複上述程式。 因此,攔截器產生和實際發佈不會在內部迴圈中發生,因為開發人員正在處理程序代碼;相反地, dotnet ef dbcontext optimizedotnet publish 都可以在 CI/CD 系統中的發佈/部署工作流程中執行。

注意

發佈目前會報告一些修剪和 NativeAOT 警告,這表示您的應用程式無法完全保證能夠正常執行。 這是預期,假設目前的 NativeAOT 支持實驗狀態;最後的非實驗性功能不會報告任何警告。

限制

不支援動態查詢

查詢先行編譯會執行原始程式碼的靜態分析,識別EF LINQ查詢併為其產生 C# 攔截器。 LINQ 允許表達高度動態的查詢,其中 LINQ 運算符是根據任意條件所組成;不幸的是,無法以靜態方式分析這類查詢,而且目前不受支援。 請考慮下列範例:

IAsyncEnumerable<Blog> GetBlogs(BlogContext context, bool applyFilter)
{
    IQueryable<Blog> query = context.Blogs.OrderBy(b => b.Id);

    if (applyFilter)
    {
        query = query.Where(b => b.Name != "foo");
    }

    return query.AsAsyncEnumerable();
}

上述查詢會分割成數個語句,並以外部參數動態撰寫 Where 運算元;這類查詢無法預先編譯。 不過,有時候可以重寫這類動態查詢,例如多個非動態查詢:

IAsyncEnumerable<Blog> GetBlogs(BlogContext context, bool applyFilter)
    => applyFilter
        ? context.Blogs.OrderBy(b => b.Id).Where(b => b.Name != "foo").AsAsyncEnumerable()
        : context.Blogs.OrderBy(b => b.Id).AsAsyncEnumerable();

由於兩個查詢都可以從頭到尾以靜態方式分析,因此先行編譯可以處理它們。

請注意,使用 NativeAOT 時,未來可能會支援動態查詢;不過,由於無法先行編譯,因此它們會繼續減緩您的應用程式啟動速度,而且相較於非 NativeAOT 執行,通常執行效率較低;這是因為 EF 內部依賴程式代碼產生來具體化資料庫結果,但使用 NativeAOT 時不支援產生程式代碼。

其他限制

  • 不支援LINQ查詢表達式語法(有時稱為「理解語法」)。
  • 產生的編譯模型和查詢攔截器目前在程序代碼大小方面可能相當大,而且需要很長的時間才能產生。 我們計劃改善這一點。
  • EF 提供者可能需要建置支援先行編譯查詢;請檢查提供者的檔,以瞭解它是否與EF的 NativeAOT 支援相容。
  • 不支援使用擷取狀態的值轉換器。

沒有 NativeAOT 的先行編譯查詢

由於EF的 NativeAOT 支援目前限制,某些應用程式可能無法使用。 不過,在發佈一般、非 NativeAOT 應用程式時,您可能能夠利用先行編譯的查詢;這可讓您至少受益於先行編譯查詢所提供的啟動時間縮減,同時能夠使用原生AOT 目前不支援的動態查詢和其他功能。

在沒有 NativeAOT 的情況下使用先行編譯的查詢只是執行下列事項:

dotnet ef dbcontext optimize --precompile-queries

如上所示,這會產生已編譯的模型和攔截器,以供預先編譯的查詢,從應用程式的啟動時間中移除其額外負荷。