Compatibilidad con NativeAOT y consultas precompiladas (experimental)
Advertencia
NativeAOT y precompilación de consultas son características muy experimentales y aún no son adecuadas para su uso en producción. La compatibilidad descrita a continuación debe verse como infraestructura para la característica final, que probablemente se publicará con EF 10. Le animamos a experimentar con el soporte técnico actual e informar sobre sus experiencias, pero se recomienda implementar aplicaciones EF NativeAOT en producción. Consulte a continuación para obtener limitaciones conocidas específicas.
.NET NativeAOT permite publicar aplicaciones .NET autocontenidas que se han compilado antes de tiempo (AOT). Al hacerlo, se ofrecen las siguientes ventajas:
- Tiempo de inicio de la aplicación significativamente más rápido
- Archivos binarios pequeños y independientes que tienen superficies de memoria más pequeñas y son más fáciles de implementar
- Ejecución de aplicaciones en entornos en los que no se admite la compilación Just-In-Time
Las aplicaciones de EF publicadas con NativeAOT se inician mucho más rápido que las mismas aplicaciones sin ella. Además de las mejoras generales de inicio de .NET que ofrece NativeAOT (es decir, no se requiere ninguna compilación JIT cada vez), EF también precompila las consultas LINQ al publicar la aplicación, de modo que no se necesite ningún procesamiento al iniciar y sql ya está disponible para su ejecución inmediata. Cuantos más consultas de EF LINQ tiene una aplicación en su código, se espera que las ganancias de inicio sean más rápidas.
Publicación de una aplicación EF NativeAOT
En primer lugar, habilite la publicación de NativeAOT para el proyecto de la siguiente manera:
<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>
La compatibilidad de EF con la ejecución de consultas LINQ en NativeAOT se basa en la precompilación de consultas: este mecanismo identifica estáticamente las consultas LINQ de EF y genera interceptores de C#, que contienen código para ejecutar cada consulta específica. Esto puede reducir significativamente el tiempo de inicio de la aplicación, ya que el trabajo pesado del procesamiento y la compilación de las consultas LINQ en SQL ya no se produce cada vez que se inicia la aplicación. En su lugar, el interceptor de cada consulta contiene el SQL finalizado para esa consulta, así como el código optimizado para materializar los resultados de la base de datos como objetos .NET.
Los interceptores de C# son actualmente una característica experimental y requieren una participación especial en el archivo del proyecto:
<PropertyGroup>
<InterceptorsNamespaces>$(InterceptorsPreviewNamespaces);Microsoft.EntityFrameworkCore.GeneratedInterceptors</InterceptorsNamespaces>
</PropertyGroup>
Por último, el paquete contiene la Microsoft.EntityFrameworkCore.Tasks
integración de MSBuild que realizará la precompilación de consultas (y generará el modelo compilado necesario) al publicar la aplicación:
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tasks" Version="9.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
Ya está listo para publicar la aplicación EF NativeAOT:
dotnet publish -r linux-arm64 -c Release
Esto muestra la publicación de una publicación nativeAOT para Linux que se ejecuta en ARM64; consulte este catálogo para buscar el identificador en tiempo de ejecución. Si desea generar los interceptores sin publicar ( por ejemplo, para examinar los orígenes generados), puede hacerlo a través del dotnet ef dbcontext optimize --precompile-queries --nativeaot
comando .
Debido a la forma en que funcionan los interceptores de C#, cualquier cambio en el origen de la aplicación los invalida y requiere repetir el proceso anterior. Como resultado, no se espera que la generación del interceptor y la publicación real se produzcan en el bucle interno, ya que el desarrollador está trabajando en el código; en su lugar, y dotnet ef dbcontext optimize
dotnet publish
se pueden ejecutar en un flujo de trabajo de publicación o implementación, en un sistema de CI/CD.
Nota:
La publicación actualmente informa de una serie de advertencias de recorte y NativeAOT, lo que significa que la aplicación no está totalmente garantizada para ejecutarse correctamente. Esto se espera dado el estado experimental actual de compatibilidad con NativeAOT; La característica final no experimental notificará ninguna advertencia.
Limitaciones
No se admiten consultas dinámicas
La precompilación de consultas realiza un análisis estático del código fuente, identificando las consultas LINQ de EF y generando interceptores de C# para ellos. LINQ permite expresar consultas altamente dinámicas, donde los operadores LINQ se componen en función de condiciones arbitrarias; Desafortunadamente, estas consultas no se pueden analizar estáticamente y actualmente no se admiten. Considere el ejemplo siguiente:
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 consulta anterior se divide entre varias instrucciones y compone dinámicamente el Where
operador en función de un parámetro externo; estas consultas no se pueden precompilar. Sin embargo, a veces es posible volver a escribir estas consultas dinámicas como varias consultas no dinámicas:
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();
Dado que las dos consultas se pueden analizar estáticamente de principio a fin, la precompilación puede controlarlas.
Tenga en cuenta que es probable que las consultas dinámicas se admita en el futuro al usar NativeAOT; sin embargo, dado que no se pueden precompilar, seguirán ralentizando el inicio de la aplicación y, por lo general, funcionarán de forma menos eficaz en comparación con la ejecución que no es NativeAOT; esto se debe a que EF se basa internamente en la generación de código para materializar los resultados de la base de datos, pero no se admite la generación de código al usar NativeAOT.
Otras limitaciones
- No se admite la sintaxis de expresión de consulta LINQ (a veces denominada "sintaxis de comprensión").
- El modelo compilado generado y los interceptores de consulta pueden ser bastante grandes en términos de tamaño de código y tardar mucho tiempo en generarse. Planeamos mejorar esto.
- Es posible que los proveedores de EF necesiten crear compatibilidad con consultas precompiladas; compruebe la documentación del proveedor para saber si es compatible con la compatibilidad de NativeAOT de EF.
- No se admiten convertidores de valores que usan el estado capturado.
Consultas precompiladas sin NativeAOT
Debido a las limitaciones actuales de la compatibilidad con NativeAOT de EF, puede que no se pueda usar para algunas aplicaciones. Sin embargo, es posible que pueda aprovechar las ventajas de las consultas precompiladas al publicar aplicaciones normales y no nativas de AOT; esto le permite beneficiarse al menos de la reducción del tiempo de inicio que ofrecen las consultas precompiladas, al tiempo que puede usar consultas dinámicas y otras características no admitidas actualmente con NativeAOT.
El uso de consultas precompiladas sin NativeAOT es simplemente cuestión de ejecutar lo siguiente:
dotnet ef dbcontext optimize --precompile-queries
Como se muestra anteriormente, esto generará un modelo compilado y interceptores para consultas que podrían precompilarse, lo que quitará su sobrecarga del tiempo de inicio de la aplicación.