Condividi tramite


Supporto nativoAOT e query precompilate (sperimentale)

Avviso

Le precompilazione nativeAOT e query sono funzionalità altamente sperimentali e non sono ancora adatte per l'uso in produzione. Il supporto descritto di seguito dovrebbe essere considerato come infrastruttura verso la funzionalità finale, che probabilmente verrà rilasciata con EF 10. È consigliabile sperimentare il supporto corrente e creare report sulle esperienze, ma è consigliabile distribuire applicazioni EF NativeAOT nell'ambiente di produzione. Vedere di seguito per limitazioni note specifiche.

.NET NativeAOT consente di pubblicare applicazioni .NET autonome compilate in anticipo (AOT). Questa operazione offre i vantaggi seguenti:

  • Tempi di avvio dell'applicazione notevolmente più veloci
  • File binari di piccole dimensioni indipendenti con footprint di memoria più piccoli e facili da distribuire
  • L'esecuzione di applicazioni in ambienti in cui la compilazione JITE non è supportata

Le applicazioni EF pubblicate con NativeAOT si avviano molto più velocemente rispetto alle stesse applicazioni senza di essa. Oltre ai miglioramenti generali di avvio di .NET offerti da NativeAOT (ovvero non è richiesta alcuna compilazione JIT ogni volta), EF precompila anche le query LINQ durante la pubblicazione dell'applicazione, in modo che non sia necessaria alcuna elaborazione durante l'avvio e SQL sia già disponibile per l'esecuzione immediata. Maggiore è il numero di query LINQ EF che un'applicazione ha nel codice, più velocemente si prevede che i guadagni di avvio siano.

Pubblicazione di un'applicazione EF NativeAOT

Prima di tutto, abilitare la pubblicazione NativeAOT per il progetto come indicato di seguito:

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

Il supporto di EF per l'esecuzione di query LINQ in NativeAOT si basa sulla precompilazione delle query: questo meccanismo identifica in modo statico le query LINQ EF e genera intercettori C#, che contengono codice per eseguire ogni query specifica. Ciò può ridurre significativamente il tempo di avvio dell'applicazione, perché l'elaborazione e la compilazione delle query LINQ in SQL non vengono più eseguite ogni volta che l'applicazione viene avviata. Al contrario, l'intercettore di ogni query contiene il codice SQL finalizzato per tale query, nonché il codice ottimizzato per materializzare i risultati del database come oggetti .NET.

Gli intercettori C# sono attualmente una funzionalità sperimentale e richiedono un consenso esplicito speciale nel file di progetto:

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

Infine, il Microsoft.EntityFrameworkCore.Tasks pacchetto contiene l'integrazione di MSBuild che eseguirà la precompilazione della query (e genera il modello compilato richiesto) quando si pubblica l'applicazione:

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

A questo punto è possibile pubblicare l'applicazione EF NativeAOT:

dotnet publish -r linux-arm64 -c Release

Viene illustrata la pubblicazione di una pubblicazione NativeAOT per Linux in esecuzione in ARM64; consultare questo catalogo per trovare l'identificatore di runtime. Se si desidera generare gli intercettori senza pubblicazione, ad esempio per esaminare le origini generate, è possibile farlo tramite il dotnet ef dbcontext optimize --precompile-queries --nativeaot comando .

A causa del funzionamento degli intercettori C#, qualsiasi modifica nell'origine dell'applicazione li invalida e richiede la ripetizione del processo precedente. Di conseguenza, la generazione dell'intercettore e la pubblicazione effettiva non dovrebbero verificarsi nel ciclo interno, perché lo sviluppatore sta lavorando al codice; In alternativa, sia dotnet ef dbcontext optimize che dotnet publish possono essere eseguiti in un flusso di lavoro di pubblicazione/distribuzione, in un sistema CI/CD.

Nota

La pubblicazione segnala attualmente una serie di avvisi di taglio e NativeAOT, il che significa che l'applicazione non è completamente garantita per l'esecuzione corretta. Questo è previsto dato lo stato sperimentale corrente del supporto NativeAOT; la funzionalità finale, non sperimentale, non segnalerà alcun avviso.

Limiti

Le query dinamiche non sono supportate

La precompilazione delle query esegue l'analisi statica del codice sorgente, identificando le query LINQ ef e generando intercettori C#. LINQ consente di esprimere query altamente dinamiche, in cui gli operatori LINQ sono composti in base a condizioni arbitrarie; queste query purtroppo non possono essere analizzate in modo statico e non sono attualmente supportate. Si consideri l'esempio seguente:

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

La query precedente è suddivisa in più istruzioni e compone in modo dinamico l'operatore Where in base a un parametro esterno. Tali query non possono essere precompilate. Tuttavia, a volte è possibile riscrivere tali query dinamiche come più query non dinamiche:

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

Poiché le due query possono essere analizzate in modo statico dall'inizio alla fine, la precompilazione può gestirle.

Si noti che le query dinamiche saranno probabilmente supportate in futuro quando si usa NativeAOT; Tuttavia, poiché non possono essere precompilati, continueranno a rallentare l'avvio dell'applicazione e in genere eseguiranno prestazioni meno efficienti rispetto all'esecuzione non NativeAOT; questo perché EF si basa internamente sulla generazione di codice per materializzare i risultati del database, ma la generazione del codice non è supportata quando si usa NativeAOT.

Altre limitazioni

  • La sintassi delle espressioni di query LINQ (talvolta definita "sintassi di comprensione") non è supportata.
  • Il modello compilato generato e gli intercettori di query possono attualmente essere piuttosto grandi in termini di dimensioni del codice e richiedere molto tempo per la generazione. Abbiamo intenzione di migliorare questo.
  • I provider EF potrebbero dover compilare il supporto per le query precompilate; Controllare la documentazione del provider per sapere se è compatibile con il supporto NativeAOT di ENTITY.
  • I convertitori di valori che usano lo stato acquisito non sono supportati.

Query precompilate senza NativeAOT

A causa delle limitazioni correnti del supporto nativeAOT di ENTITY, potrebbe non essere utilizzabile per alcune applicazioni. Tuttavia, è possibile sfruttare le query precompilate durante la pubblicazione di normali applicazioni non NativeAOT; ciò consente di sfruttare almeno la riduzione del tempo di avvio offerta dalle query precompilate, pur essendo in grado di usare query dinamiche e altre funzionalità attualmente non supportate con NativeAOT.

L'uso di query precompilate senza NativeAOT è semplicemente una questione di esecuzione:

dotnet ef dbcontext optimize --precompile-queries

Come illustrato in precedenza, verrà generato un modello compilato e intercettori per le query che potrebbero essere precompilate, rimuovendone il sovraccarico dall'ora di avvio dell'applicazione.