NativeAOT のサポートとプリコンパイル済みクエリ (試験段階)
警告
NativeAOT とクエリプリコンパイルは試験的な機能であり、運用環境での使用にはまだ適していません。 以下で説明するサポートは、最終的な機能に向けたインフラストラクチャと見なす必要があります。これは、EF 10 でリリースされる可能性があります。 現在のサポートを試し、エクスペリエンスを報告することをお勧めしますが、運用環境で EF NativeAOT アプリケーションをデプロイすることはお勧めします。 特定の既知の制限事項については、以下を参照してください。
.NET ネイティブAOT では、事前にコンパイルされた自己完結型 .NET アプリケーション (AOT) を発行できます。 これを行うと、次の利点があります。
- アプリケーションの起動時間を大幅に短縮
- メモリフットプリントが小さく、デプロイが容易な、小さな自己完結型バイナリ
- Just-In-Time コンパイルがサポートされていない環境でのアプリケーションの実行
NativeAOT で公開された EF アプリケーションは、それなしで同じアプリケーションよりもはるかに高速に起動します。 NativeAOT が提供する一般的な .NET スタートアップの機能強化 (つまり、毎回 JIT コンパイルは必要ありません) に加えて、EF ではアプリケーションの発行時に LINQ クエリもプリコンパイルされるため、起動時に処理は必要なく、SQL は既にすぐに実行できます。 アプリケーションのコードに含まれる EF LINQ クエリが多いほど、スタートアップの向上が期待されます。
EF NativeAOT アプリケーションの発行
まず、次のようにプロジェクトの NativeAOT 発行を有効にします。
<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>
NativeAOT での LINQ クエリ実行に対する EF のサポートは、 query プリコンパイルに依存します: このメカニズムは EF LINQ クエリを静的に識別し、C# interceptors を生成します。これには、各特定のクエリを実行するコードが含まれています。 これにより、アプリケーションの起動時間が大幅に短縮される可能性があります。LINQ クエリを SQL にコンパイルする処理が大量に発生し、アプリケーションが起動するたびに発生しなくなったのでです。 代わりに、各クエリのインターセプターには、そのクエリの最終版 SQL と、データベースの結果を .NET オブジェクトとして具体化するための最適化されたコードが含まれています。
C# インターセプターは現在試験段階の機能であり、プロジェクト ファイルに特別なオプトインが必要です。
<PropertyGroup>
<InterceptorsNamespaces>$(InterceptorsPreviewNamespaces);Microsoft.EntityFrameworkCore.GeneratedInterceptors</InterceptorsNamespaces>
</PropertyGroup>
最後に、 Microsoft.EntityFrameworkCore.Tasks
パッケージには MSBuild 統合 が含まれておりアプリケーションの発行時にクエリのプリコンパイルを実行し、必要なコンパイル済みモデルを生成します。
<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 optimize
と dotnet publish
の両方を、CI/CD システムの発行/デプロイ ワークフローで実行できます。
Note
現在発行すると、トリミングと 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();
2 つのクエリはそれぞれ最初から最後まで静的に分析できるため、プリコンパイルで処理できます。
NativeAOT を使用する場合、動的クエリは将来サポートされる可能性があることに注意してください。ただし、プリコンパイルできないため、アプリケーションの起動速度が低下し続け、一般に NativeAOT 以外の実行と比較してパフォーマンスが低下します。これは、EF は内部的にコード生成に依存してデータベースの結果を具体化するが、NativeAOT を使用する場合はコード生成がサポートされないためです。
その他の制限事項
- LINQ クエリ式の構文 ("理解構文" とも呼ばれる) はサポートされていません。
- 生成されたコンパイル済みモデルとクエリ インターセプターは、現在、コード サイズの観点から非常に大きく、生成に時間がかかる場合があります。 これを改善する予定です。
- EF プロバイダーでは、プリコンパイル済みクエリのサポートを構築する必要がある場合があります。プロバイダーのドキュメントを調べて、EF の NativeAOT サポートと互換性があるかどうかを確認します。
- キャプチャされた状態を使用する値コンバーターはサポートされていません。
NativeAOT を使用しないプリコンパイル済みクエリ
EF の NativeAOT サポートの現在の制限により、一部のアプリケーションでは使用できない場合があります。 ただし、通常の NativeAOT 以外のアプリケーションを発行するときに、プリコンパイル済みクエリを利用できる場合があります。これにより、ネイティブAOT で現在サポートされていない動的クエリやその他の機能を使用できる一方で、プリコンパイル済みクエリが提供する起動時間の短縮のメリットを少なくとも得ることができます。
NativeAOT を使用せずにプリコンパイル済みクエリを使用することは、単に次の実行の問題です。
dotnet ef dbcontext optimize --precompile-queries
上に示したように、これにより、プリコンパイル可能なクエリのコンパイル済みモデルとインターセプターが生成され、アプリケーションの起動時間からオーバーヘッドが除去されます。
.NET