Поделиться через


Поддержка NativeAOT и предварительно скомпилированные запросы (экспериментальные)

Предупреждение

Предкомпиляция NativeAOT и запросы являются высоко экспериментальной функцией и еще не подходят для использования в рабочей среде. Поддержку, описанную ниже, следует рассматривать как инфраструктуру в сторону окончательной функции, которая, скорее всего, будет выпущена с EF 10. Мы рекомендуем поэкспериментировать с текущей поддержкой и отчетом о ваших интерфейсах, но рекомендуется развертывать приложения EF NativeAOT в рабочей среде. См. ниже сведения о конкретных известных ограничениях.

.NET NativeAOT позволяет публиковать автономные приложения .NET, скомпилированные заранее (AOT). Это дает следующие преимущества:

  • Значительно быстрее время запуска приложения
  • Небольшие автономные двоичные файлы с меньшим объемом памяти и проще развертывать
  • Запуск приложений в средах, где не поддерживается JIT-компиляция

Приложения EF, опубликованные с помощью NativeAOT, запускают гораздо быстрее, чем те же приложения без него. Помимо общих улучшений запуска .NET, предоставляемых NativeAOT (т. е. не требуется компиляция JIT каждый раз), EF также предварительно компилирует запросы LINQ при публикации приложения, чтобы при запуске не требуется обработка, а SQL уже доступен для немедленного выполнения. Чем больше EF LINQ запрашивает приложение в своем коде, тем быстрее, чем ожидается увеличение запуска.

Публикация приложения EF NativeAOT

Сначала включите публикацию NativeAOT для проекта следующим образом:

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

Поддержка выполнения запросов EF LINQ в NativeAOT зависит от предварительной компиляции запросов: этот механизм статически определяет запросы EF LINQ и создает перехватчики C#, содержащие код для выполнения каждого конкретного запроса. Это может значительно сократить время запуска приложения, так как тяжелый подъем обработки и компиляция запросов 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

В этой статье показано, как опубликовать публикацию NativeAOT для Linux, работающей в ARM64; Обратитесь к этому каталогу , чтобы найти идентификатор среды выполнения. Если вы хотите создать перехватчики без публикации, например для проверки созданных источников, это можно сделать с помощью dotnet ef dbcontext optimize --precompile-queries --nativeaot команды.

Из-за того, как работают перехватчики C#, любые изменения в источнике приложения недействительны и требуют повторения приведенного выше процесса. В результате создание перехватчика и фактическая публикация не должны произойти во внутреннем цикле, так как разработчик работает над кодом; вместо этого dotnet ef dbcontext optimize dotnet 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; это позволяет по крайней мере воспользоваться сокращением времени запуска, которое предлагает предварительно скомпилированные запросы, при этом возможность использовать динамические запросы и другие функции, которые в настоящее время не поддерживаются в NativeAOT.

Использование предварительно скомпилированных запросов без NativeAOT — это просто вопрос выполнения следующих действий:

dotnet ef dbcontext optimize --precompile-queries

Как показано выше, это создаст скомпилированную модель и перехватчики для запросов, которые могут быть предварительно компилированы, удаляя свои затраты из времени запуска приложения.