Consideraciones de rendimiento (Entity Framework)
En este tema se describen las características de rendimiento de ADO.NET Entity Framework y se facilitan algunas consideraciones para ayudar a mejorar el rendimiento de las aplicaciones de Entity Framework.
Fases de la ejecución de consultas
Para entender mejor el rendimiento de las consultas en Entity Framework, resulta útil comprender las operaciones que se realizan cuando se ejecuta una consulta en un modelo conceptual y se devuelven datos como objetos. En la tabla siguiente se describe esta serie de operaciones.
Operación | Costo relativo | Frecuencia | Comentarios |
---|---|---|---|
Cargar los metadatos |
Moderado |
Una vez en cada dominio de aplicación. |
Los metadatos de la asignación y del modelo usados por Entity Framework se cargan en una instancia de MetadataWorkspace. Estos metadatos se almacenan globalmente en caché y están disponibles para otras instancias de ObjectContext en el mismo dominio de aplicación. |
Abrir la conexión de la base de datos |
Moderado1 |
Según sea necesario. |
Dado que una conexión abierta con la base de datos consume recursos valiosos, Entity Framework abre y cierra dicha conexión según sea necesario. También puede abrir explícitamente la conexión. Para obtener más información, vea Administrar conexiones y transacciones (Entity Framework). |
Generar vistas |
Alto |
Una vez en cada dominio de aplicación. (Se pueden generar previamente). |
Antes de que Entity Framework pueda ejecutar una consulta en un modelo conceptual o guardar los cambios realizados en el origen de datos, debe generar un conjunto de vistas de consulta locales para obtener acceso a la base de datos. Debido al alto costo de generar estas vistas, es posible generarlas previamente y agregarlas al proyecto en tiempo de diseño. Para obtener más información, vea Cómo: Generar previamente vistas para mejorar el rendimiento de las consultas. |
Preparar la consulta |
Moderado2 |
Una vez para cada consulta única. |
Incluye los costos para crear el comando de consulta, generar un árbol de comandos basado en los metadatos de asignación y del modelo, y definir la forma de los datos devueltos. Dado que los comandos de consulta de Entity SQL se almacenan en la caché, las ejecuciones posteriores de la misma consulta tardarán incluso menos tiempo. También puede usar consultas LINQ compiladas para reducir este costo en ejecuciones posteriores. Para obtener más información, vea Consultas compiladas (LINQ to Entities). Para obtener información general acerca de la ejecución de consultas LINQ, vea LINQ to Entities. |
Ejecutar la consulta |
Bajo2 |
Una vez para cada consulta. |
El costo de ejecutar el comando en el origen de datos usando el proveedor de datos de ADO.NET. Dado que la mayoría de los orígenes de datos almacenan en la caché los planes de consulta, las posteriores ejecuciones de la misma consulta pueden tardar menos tiempo. |
Cargar y validar los tipos |
Bajo3 |
Una vez para cada instancia de ObjectContext. |
Los tipos se cargan y se validan con los tipos definidos por el modelo conceptual. |
Seguimiento |
Bajo3 |
Una vez para cada objeto devuelto por una consulta. 4 |
Si una consulta usa la opción de combinación NoTracking, esta fase no afecta al rendimiento. Si la consulta usa la opción de combinación AppendOnly, PreserveChanges o OverwriteChanges, el seguimiento de los resultados de la consulta se realiza en la instancia de ObjectStateManager. Se genera una instancia de EntityKey para cada objeto devuelto por la consulta al que se ha hecho un seguimiento y se usa para crear un objeto ObjectStateEntry en la instancia de ObjectStateManager. Si se encuentra un objeto ObjectStateEntry existente para la instancia de EntityKey, se devuelve dicho objeto. Si se usa la opción PreserveChanges o OverwriteChanges, el objeto se actualiza antes de ser devuelto. Para obtener más información, vea Resolución de identidades, administración de estados y seguimiento de cambios (Entity Framework). |
Materializar los objetos |
Moderado3 |
Una vez para cada objeto devuelto por una consulta. 4 |
El proceso de leer el objeto DbDataReader devuelto, crear los objetos y establecer valores de propiedad basados en los valores de cada instancia de la clase DbDataRecord. Si el objeto ya existe en ObjectContext y la consulta usa las opciones de combinación AppendOnly o PreserveChanges, esta fase no afecta al rendimiento. Para obtener más información, vea Resolución de identidades, administración de estados y seguimiento de cambios (Entity Framework). |
1 Cuando un proveedor de origen de datos implementa la agrupación de conexiones, el costo de abrir una conexión se distribuye entre todo el grupo. El proveedor de datos .NET para SQL Server admite la agrupación de conexiones.
2 El costo aumenta con la complejidad de la consulta.
3 El costo total aumenta proporcionalmente al número de objetos devueltos por la consulta.
4 Esta sobrecarga no es necesaria para las consultas de EntityClient, ya que estas devuelven una instancia de EntityDataReader en lugar de objetos. Para obtener más información, vea Proveedor de EntityClient para Entity Framework.
Consideraciones adicionales
A continuación se muestran otras consideraciones que pueden afectar al rendimiento de las aplicaciones de Entity Framework.
Ejecución de la consulta
Dado que las consultas usan muchos recursos, considere detenidamente en qué punto del código y en qué equipo se va ejecutar una consulta.
Ejecución diferida frente a ejecución inmediata
Cuando se crea una instancia de ObjectQuery o una consulta LINQ, la consulta no se puede ejecutar inmediatamente. La ejecución de la consulta se aplaza hasta que se necesiten los resultados, como por ejemplo, durante una enumeración foreach (C#) o For Each (Visual Basic) o cuando se asigna para rellenar una colección List. La ejecución de la consulta comienza inmediatamente cuando se llama al método Execute de una ObjectQuery o cuando se llama a un método LINQ que devuelve una consulta singleton, como First o Any. Para obtener más información, vea Consultas de objeto (Entity Framework) y Ejecución de la consulta (LINQ to Entities).
Ejecución de consultas LINQ en el cliente
Aunque la ejecución de un consulta LINQ se produce en el equipo que hospeda el origen de datos, algunas partes de dicha consulta se pueden evaluar en el equipo cliente. Para obtener más información, vea la sección Ejecución en el almacén de Ejecución de la consulta (LINQ to Entities).
Complejidad de la asignación y de la consulta
La complejidad de las consultas individuales y de la asignación en el modelo de entidades tendrá un efecto significativo en el rendimiento de las consultas.
Complejidad de la asignación
Los modelos con una complejidad superior a la de una asignación unívoca simple entre entidades del modelo conceptual y tablas del modelo de almacenamiento generan comandos más complejos.
Complejidad de la consulta
Las consultas que requieren un gran número de combinaciones en los comandos que se ejecutan en el origen de datos o que devuelven una gran cantidad de datos pueden afectar al rendimiento de las maneras siguientes:
Las consultas realizadas en un modelo conceptual y que parecen sencillas pueden ocasionar la ejecución de consultas más complejas en el origen de datos. Esto se puede producir porque Entity Framework traduce una consulta en un modelo conceptual en una consulta equivalente en el origen de datos. Cuando un único conjunto de entidades en el modelo conceptual se asigna a varias tablas en el origen de datos, o cuando una relación entre entidades se asigna a una tabla de combinación, el comando de consulta ejecutado en la consulta del origen de datos puede requerir una o más combinaciones.
Nota: Use el método ToTraceString de las clases EntityCommand o ObjectQuery para ver los comandos que se ejecutan en el origen de datos para una consulta determinada.Para obtener más información, vea Cómo ver los comandos de almacenamiento (Entity Framework). Las consultas de Entity SQL anidadas pueden crear combinaciones en el servidor y devolver un gran número de filas.
A continuación se muestra un ejemplo de una consulta anidada en una cláusula de proyección:
SELECT c, (SELECT c, (SELECT c FROM AdventureWorksModel.Vendor AS c ) As Inner2 FROM AdventureWorksModel.JobCandidate AS c ) As Inner1 FROM AdventureWorksModel.EmployeeDepartmentHistory AS c
Además, provocan que la canalización de la consulta genere una consulta única con duplicación de objetos en las consultas anidadas. Debido a esto, una sola columna se puede duplicar varias veces. En algunas bases de datos, incluyendo SQL Server, esto puede ocasionar que la tabla TempDB crezca demasiado, lo que puede disminuir el rendimiento del servidor. Conviene tener cuidado al ejecutar consultas anidadas.
Las consultas que devuelven una gran cantidad de datos pueden disminuir el rendimiento si el cliente está realizando operaciones que tienen un consumo de recursos proporcional al tamaño del conjunto de resultados. En casos como este, considere la posibilidad de limitar la cantidad de datos devuelta por la consulta. Para obtener más información, vea Cómo: Paginar a través de los resultados de una consulta (Entity Framework).
Los comandos generados automáticamente por Entity Framework pueden ser más complejos que los comandos similares escritos explícitamente por un desarrollador de bases de datos. Si necesita un control explícito sobre los comandos ejecutados en el origen de datos, considere la posibilidad de definir una asignación a una función con valores de tabla o a un procedimiento almacenado.
Relaciones
Para obtener un rendimiento óptimo de las consultas, debe definir las relaciones entre entidades como asociaciones en el modelo de entidades y también como relaciones lógicas en el origen de datos.
Rutas de la consulta
De forma predeterminada, al ejecutar una consulta ObjectQuery, no se devuelven los objetos relacionados (aunque los propios objetos que representan las relaciones lo son). Puede cargar objetos relacionados de una de las tres maneras siguientes:
Establezca la ruta de acceso de la consulta antes de que se ejecute ObjectQuery.
Llame al método Load sobre la propiedad de navegación expuesta por el objeto.
Establezca la opción LazyLoadingEnabled del ObjectContext en true. Observe que esto se realiza automáticamente al generar código del nivel de objeto con el Entity Data Model Designer. Para obtener más información, vea Generated Code Overview.
Cuando considere qué opción se debe usar, sea consciente de que hay una correlación entre el número de solicitudes a la base de datos y la cantidad de datos devueltos en una sola consulta. Para obtener más información, vea Cargar objetos relacionados (Entity Framework).
Usar las rutas de la consulta
Las rutas de la consulta definen el gráfico de los objetos devueltos por una consulta. Al definir la ruta de una consulta, solo se requiere una solicitud única en la base de datos para que se devuelvan todos los objetos definidos por la ruta. El uso de rutas de consulta puede dar lugar a la ejecución de comandos complejos en el origen de datos partiendo de consultas de objeto aparentemente simples. Esto sucede porque se requieren una o varias combinaciones para devolver los objetos relacionados en una sola consulta. Esta complejidad es mayor en consultas con un modelo de entidades complejo, como una entidad con herencia o una ruta que incluye relaciones varios a varios.
Nota: |
---|
Use el método ToTraceString para ver el comando que se generará mediante una ObjectQuery.Para obtener más información, vea Cómo ver los comandos de almacenamiento (Entity Framework). |
Cuando la ruta de una consulta incluye demasiados objetos relacionados o cuando los objetos contienen demasiados datos de fila, es posible que el origen de datos no pueda completar la consulta. Esto sucede si la consulta requiere un almacenamiento temporal intermedio que supera la capacidad del origen de datos. Cuando sucede esto, se puede reducir la complejidad de la consulta del origen de datos cargando explícitamente los objetos relacionados.
Cargar explícitamente los objetos relacionados
Puede cargar explícitamente los objetos relacionados llamando al método Load de una propiedad de navegación que devuelve una EntityCollection o EntityReference. La carga explícita de objetos requiere un viaje de ida y vuelta a la base de datos cada vez que se llama al método Load.
Nota: |
---|
Si llama al método Load mientras se recorre en bucle una colección de objetos devueltos, como cuando se usa la instrucción foreach (For Each en Visual Basic), el proveedor específico del origen de datos debe admitir varios conjuntos de resultados activos en una sola conexión.En una base de datos de SQL Server, debe especificar un valor MultipleActiveResultSets = true en la cadena de conexión del proveedor.
|
También puede utilizar el método LoadProperty cuando no tenga propiedades EntityReference o EntityCollection en entidades. Esto es útil cuando está usando entidades POCO.
Aunque la carga explícita de objetos relacionados reducirá el número de combinaciones y la cantidad de datos redundantes, Load requiere varias conexiones a la base de datos, lo que puede resultar costoso cuando se carga explícitamente un gran número de objetos.
Guardar los cambios
Cuando se llama al método SaveChanges de una instancia de ObjectContext, se genera un comando de creación, actualización o eliminación independiente para cada objeto agregado, actualizado o eliminado en el contexto. Estos comandos se ejecutan en el origen de datos en una transacción única. Al igual que ocurre en las consultas, el rendimiento de las operaciones de creación, actualización y eliminación depende de la complejidad de la asignación en el modelo conceptual.
Transacciones distribuidas
Las operaciones de una transacción explícita que requieren recursos administrados por el coordinador de transacciones distribuidas (DTC) serán mucho más caras que las operaciones similares que no requieren DTC. La promoción a DTC se producirá en las situaciones siguientes:
Una transacción explícita con una operación en una base de datos de SQL Server 2000 u otro origen de datos que siempre promueve transacciones explícitas a DTC.
Una transacción explícita con una operación en SQL Server 2005 cuando Entity Framework administra la conexión. Esto sucede porque SQL Server 2005 promueve a DTC cada vez que se cierra y se vuelve a abrir una conexión dentro de una transacción única, que es el comportamiento predeterminado de Entity Framework . Esta promoción a DTC no se produce si se usa SQL Server 2008. Para evitar esta promoción al usar SQL Server 2005, se debe abrir y cerrar explícitamente la conexión dentro de la transacción. Para obtener más información, vea Administrar conexiones y transacciones (Entity Framework).
Las transacciones explícitas se usan cuando se ejecutan una o varias operaciones dentro de una transacción System.Transactions. Para obtener más información, vea Administrar conexiones y transacciones (Entity Framework).
Estrategias para mejorar el rendimiento
Para mejorar el rendimiento global de las consultas en Entity Framework, use las estrategias siguientes.
Generar previamente las vistas
La generación de vistas basadas en un modelo de entidades supone un costo significativo la primera vez que una aplicación ejecuta una consulta. Emplee la utilidad EdmGen.exe para generar previamente las vistas en forma de archivo de código de Visual Basic o de C# que se puede agregar al proyecto durante el diseño. Las vistas generadas previamente se validan en tiempo de ejecución para garantizar su coherencia con la versión actual del modelo de entidades especificado. Para obtener más información, vea Cómo: Generar previamente vistas para mejorar el rendimiento de las consultas.
Considerar la posibilidad de usar la opción de combinación NoTracking en las consultas
El seguimiento de los objetos devueltos en el contexto del objeto tiene un costo implícito. Para detectar cambios en los objetos y garantizar que varias solicitudes para la misma entidad lógica devuelvan la misma instancia del objeto, es necesario que los objetos se adjunten a una instancia de ObjectContext. Si no tiene previsto realizar actualizaciones o eliminaciones en los objetos y no necesita la administración de identidades, considere la posibilidad de usar las opciones de combinación NoTracking al ejecutar las consultas.
Usar consultas LINQ compiladas
Cuando una aplicación ejecuta muchas veces consultas que tienen una estructura similar en Entity Framework, se suele mejorar el rendimiento si se compila la consulta una vez y se ejecuta varias veces con parámetros diferentes. Por ejemplo, una aplicación puede tener que recuperar todos los clientes de una ciudad determinada; el usuario especifica en un formulario la ciudad en tiempo de ejecución. LINQ to Entities admite el uso de consultas compiladas para este fin. La consulta compila una vez solo durante la primera ejecución. El conjunto de opciones de combinación de la consulta en el momento de la compilación no se puede cambiar posteriormente.
Para obtener más información, vea Consultas compiladas (LINQ to Entities).
Devolver la cantidad correcta de datos
En algunos escenarios, la especificación de una ruta de consulta mediante el método Include es mucho más rápida porque requiere menos recorridos de ida y vuelta a la base de datos. Sin embargo, en otros escenarios, los recorridos adicionales de ida y vuelta a la base de datos para cargar los objetos relacionados pueden ser más rápidos porque las consultas más sencillas con menos combinaciones producen una menor cantidad de datos redundantes. Por ello, se recomienda probar el rendimiento de varias maneras para recuperar los objetos relacionados. Para obtener más información, vea Cargar objetos relacionados (Entity Framework).
Para evitar devolver demasiados datos en una sola consulta, considere la posibilidad de paginar los resultados de la consulta en grupos que sean más fáciles de administrar. Para obtener más información, vea Cómo: Paginar a través de los resultados de una consulta (Entity Framework).
Limitar el ámbito de ObjectContext
En la mayoría de los casos, deberá crear una instancia de ObjectContext dentro de una instrucción using
(Using…End Using
en Visual Basic). Esto puede aumentar el rendimiento garantizando que los recursos asociados al contexto del objeto se eliminan automáticamente cuando el código sale del bloque de instrucciones. Sin embargo, cuando los controles se enlazan a objetos administrados por el contexto del objeto, se debe mantener la instancia de ObjectContext mientras se necesite el enlace, y eliminarla posteriormente de forma manual. Para obtener más información, vea Administrar conexiones y transacciones (Entity Framework).
Considerar la posibilidad de abrir manualmente la conexión de la base de datos
Cuando la aplicación ejecuta una serie de consultas de objeto o llama con frecuencia al método SaveChanges para conservar las operaciones de creación, actualización y eliminación en el origen de datos, Entity Framework debe abrir y cerrar continuamente la conexión al origen de datos. En estos casos, considere la posibilidad de abrir manualmente la conexión al comienzo de estas operaciones y cerrarla o eliminarla una vez finalizadas dichas operaciones. Para obtener más información, vea Administrar conexiones y transacciones (Entity Framework).
Datos de rendimiento
Algunos datos de rendimiento para Entity Framework se publican en las entradas siguientes del blog del equipo de ADO.NET:
Explorar el rendimiento de ADO.NET Entity Framework - Parte 1
Explorar el rendimiento de ADO.NET Entity Framework – Parte 2
Vea también
Conceptos
Consideraciones de desarrollo e implementación (Entity Framework)