Udostępnij za pośrednictwem


Obsługa funkcji NativeAOT i wstępnie skompilowane zapytania (eksperymentalne)

Ostrzeżenie

Prekompilacja nativeAOT i zapytań są wysoce eksperymentalne i nie są jeszcze odpowiednie do użytku produkcyjnego. Pomoc techniczna opisana poniżej powinna być postrzegana jako infrastruktura w kierunku ostatniej funkcji, która prawdopodobnie zostanie wydana z programem EF 10. Zachęcamy do eksperymentowania z bieżącą pomocą techniczną i raportem na temat Twoich środowisk, ale zalecamy wdrożenie aplikacji EF NativeAOT w środowisku produkcyjnym. Poniżej przedstawiono konkretne znane ograniczenia.

Platforma .NET NativeAOT umożliwia publikowanie samodzielnych aplikacji .NET, które zostały skompilowane przed czasem (AOT). Zapewnia to następujące korzyści:

  • Znacznie krótszy czas uruchamiania aplikacji
  • Małe, samodzielne pliki binarne, które mają mniejsze zużycie pamięci i są łatwiejsze do wdrożenia
  • Uruchamianie aplikacji w środowiskach, w których kompilacja just in time nie jest obsługiwana

Aplikacje EF opublikowane za pomocą funkcji NativeAOT są uruchamiane znacznie szybciej niż te same aplikacje bez niej. Oprócz ogólnych ulepszeń uruchamiania platformy .NET, które oferuje nativeAOT (tj. nie jest wymagana kompilacja JIT za każdym razem), program EF prekompiluje również zapytania LINQ podczas publikowania aplikacji, aby nie było wymagane żadne przetwarzanie podczas uruchamiania, a program SQL jest już dostępny do natychmiastowego wykonania. Tym więcej zapytań EF LINQ aplikacja ma w kodzie, tym szybciej oczekuje się, że zyski z uruchamiania będą.

Publikowanie aplikacji EF NativeAOT

Najpierw włącz publikowanie nativeAOT dla projektu w następujący sposób:

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

Obsługa wykonywania zapytań LINQ w ramach funkcji NativeAOT jest oparta na prekompilacji zapytań: ten mechanizm statycznie identyfikuje zapytania EF LINQ i generuje przechwytniki języka C#, które zawierają kod do wykonania poszczególnych zapytań. Może to znacząco zmniejszyć czas uruchamiania aplikacji, ponieważ duże obciążenie przetwarzania i kompilowania zapytań LINQ w języku SQL nie odbywa się już za każdym razem, gdy aplikacja się uruchamia. Zamiast tego przechwytywanie każdego zapytania zawiera sfinalizowany kod SQL dla tego zapytania, a także zoptymalizowany kod w celu materializowania wyników bazy danych jako obiektów platformy .NET.

Przechwytywanie języka C# jest obecnie funkcją eksperymentalną i wymaga specjalnej zgody w pliku projektu:

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

Na koniec pakiet zawiera integrację programu MSBuild, Microsoft.EntityFrameworkCore.Tasks która przeprowadzi wstępne kompilowanie zapytania (i wygeneruje wymagany skompilowany model) podczas publikowania aplikacji:

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

Teraz możesz opublikować aplikację EF NativeAOT:

dotnet publish -r linux-arm64 -c Release

Spowoduje to opublikowanie publikacji nativeAOT dla systemu Linux działającego w usłudze ARM64; zapoznaj się z tym wykazem , aby znaleźć identyfikator środowiska uruchomieniowego. Jeśli chcesz wygenerować przechwytniki bez publikowania — na przykład w celu zbadania wygenerowanych źródeł — możesz to zrobić za pomocą dotnet ef dbcontext optimize --precompile-queries --nativeaot polecenia .

Ze względu na sposób działania przechwytywania języka C# wszelkie zmiany w źródle aplikacji unieważniają je i wymagają powtórzenia powyższego procesu. W związku z tym generowanie przechwytywania i rzeczywiste publikowanie nie powinno się zdarzyć w pętli wewnętrznej, ponieważ deweloper pracuje nad kodem; zamiast tego w przepływie pracy publikowania/wdrażania można wykonać zarówno dotnet ef dbcontext optimize w systemie ciągłej integracji/ciągłego wdrażania, jak i dotnet publish .

Uwaga

Publikowanie obecnie raportuje szereg ostrzeżeń dotyczących przycinania i natywnego stosowania funkcji AOT, co oznacza, że aplikacja nie jest w pełni gwarantowana do prawidłowego działania. Jest to oczekiwane, biorąc pod uwagę bieżący stan eksperymentalny obsługi NativeAOT; ostatnia funkcja nie eksperymentalna nie będzie zgłaszać żadnych ostrzeżeń.

Ograniczenia

Zapytania dynamiczne nie są obsługiwane

Wstępne kompilowanie zapytań wykonuje statyczną analizę kodu źródłowego, identyfikując zapytania EF LINQ i generując przechwytniki języka C#. LINQ umożliwia wyrażanie wysoce dynamicznych zapytań, w których operatory LINQ składają się na podstawie dowolnych warunków; Takie zapytania nie mogą być analizowane statycznie i obecnie nie są obsługiwane. Rozważmy następujący przykład:

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

Powyższe zapytanie jest podzielone na kilka instrukcji i dynamicznie komponuje Where operator na podstawie parametru zewnętrznego. Takie zapytania nie mogą być wstępnie skompilowane. Czasami jednak można ponownie napisać takie zapytania dynamiczne jak wiele zapytań niedynamicznych:

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();

Ponieważ te dwa zapytania mogą być statycznie analizowane od początku do końca, prekompilacja może je obsłużyć.

Należy pamiętać, że zapytania dynamiczne będą prawdopodobnie obsługiwane w przyszłości podczas korzystania z funkcji NativeAOT; jednak ponieważ nie można ich wstępnie skompilować, będą nadal spowalniać uruchamianie aplikacji, a także zazwyczaj będą działać mniej wydajnie w porównaniu z wykonywaniem funkcji NativeAOT; Wynika to z faktu, że program EF wewnętrznie opiera się na generowaniu kodu w celu materializowania wyników bazy danych, ale generowanie kodu nie jest obsługiwane w przypadku korzystania z funkcji NativeAOT.

Inne ograniczenia

  • Składnia wyrażenia zapytania LINQ (czasami nazywana "składnią zrozumienia") nie jest obsługiwana.
  • Wygenerowany model i przechwytywanie zapytań mogą obecnie być dość duże pod względem rozmiaru kodu i trwa długo. Planujemy to poprawić.
  • Dostawcy ef może wymagać skompilowania obsługi wstępnie skompilowanych zapytań; Sprawdź dokumentację dostawcy, aby dowiedzieć się, czy jest ona zgodna z obsługą natywnej funkcji EFAOT.
  • Konwertery wartości używające stanu przechwyconego nie są obsługiwane.

Wstępnie skompilowane zapytania bez funkcji NativeAOT

Ze względu na bieżące ograniczenia obsługi natywnej funkcji EFAOT może nie być możliwe do użycia w przypadku niektórych aplikacji. Można jednak korzystać ze wstępnie skompilowanych zapytań podczas publikowania zwykłych, nienatywnych aplikacji AOT; Dzięki temu można przynajmniej skorzystać z redukcji czasu uruchamiania, która oferuje wstępnie skompilowane zapytania, jednocześnie umożliwiając korzystanie z zapytań dynamicznych i innych funkcji, które nie są obecnie obsługiwane w przypadku funkcji NativeAOT.

Używanie wstępnie skompilowanych zapytań bez funkcji NativeAOT jest po prostu kwestią wykonywania następujących czynności:

dotnet ef dbcontext optimize --precompile-queries

Jak pokazano powyżej, spowoduje to wygenerowanie skompilowanego modelu i przechwytujących dla zapytań, które mogą zostać wstępnie skompilowane, co spowoduje usunięcie obciążenia z czasu uruchamiania aplikacji.