Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este tema se describen las características de rendimiento de ADO.NET Entity Framework y se proporcionan algunas consideraciones para ayudar a mejorar el rendimiento de las aplicaciones de Entity Framework.
Fases de ejecución de consultas
Para comprender mejor el rendimiento de las consultas en Entity Framework, resulta útil comprender las operaciones que se producen cuando una consulta se ejecuta en un modelo conceptual y devuelve datos como objetos. En la tabla siguiente se describe esta serie de operaciones.
Operación | Costo relativo | Frecuencia | Comentarios |
---|---|---|---|
Carga de metadatos | Moderada | 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 en caché globalmente y están disponibles para otras instancias de en el mismo dominio de ObjectContext aplicación. |
Apertura de la conexión de base de datos | Moderado1 | Según sea necesario. | Dado que una conexión abierta a la base de datos consume un recurso valioso, Entity Framework se abre y cierra la conexión de base de datos solo según sea necesario. También puede abrir explícitamente la conexión. Para obtener más información, consulte Administración de conexiones y transacciones. |
Generando vistas | Alto | Una vez en cada dominio de aplicación. (Se puede generar previamente). | Para que Entity Framework pueda ejecutar una consulta en un modelo conceptual o guardar los cambios en el origen de datos, debe generar un conjunto de vistas de consulta locales para acceder a la base de datos. Debido al alto costo de generar estas vistas, puede generar previamente las vistas y agregarlas al proyecto en tiempo de diseño. Para obtener más información, consulte Cómo: Generar vistas previamente para mejorar el rendimiento de las consultas. |
Preparación de 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 metadatos de modelo y asignación, y definir la forma de los datos devueltos. Dado que ahora los comandos de consulta de Entity SQL y las consultas LINQ se almacenan en caché, las ejecuciones posteriores de la misma consulta tardan menos tiempo. Todavía puede usar consultas LINQ compiladas para reducir este costo en ejecuciones posteriores y consultas compiladas puede ser más eficaz que las consultas LINQ que se almacenan automáticamente en caché. Para obtener más información, vea Consultas compiladas (LINQ to Entities). Para obtener información general sobre la ejecución de consultas LINQ, vea LINQ to Entities.
Nota: Las consultas LINQ to Entities que aplican el Enumerable.Contains operador a colecciones en memoria no se almacenan automáticamente en caché. Tampoco se permite parametrizar colecciones en memoria en consultas LINQ compiladas. |
Ejecución de la consulta | Bajo2 | Una vez para cada consulta. | Costo de ejecutar el comando en el origen de datos mediante el proveedor de datos ADO.NET. Dado que la mayoría de los orígenes de datos almacenan en caché los planes de consulta, las ejecuciones posteriores de la misma consulta pueden tardar incluso menos tiempo. |
Carga y validación de tipos | Bajo3 | Una vez para cada ObjectContext instancia. | Los tipos se cargan y validan con los tipos que define el modelo conceptual. |
Seguimiento | Bajo3 | Una vez para cada objeto que devuelve una consulta. 4 | Si una consulta usa la NoTracking opción de combinación, esta fase no afecta al rendimiento. Si la consulta usa la opción de combinación AppendOnly, PreserveChanges o OverwriteChanges, se realiza un seguimiento de los resultados de la consulta en ObjectStateManager. Se genera un EntityKey para cada objeto con seguimiento que devuelve la consulta y se utiliza para crear un ObjectStateEntry en el ObjectStateManager. Si se puede encontrar un existente ObjectStateEntry para EntityKey, se devuelve el objeto existente. Si se usa la PreserveChangesopción , o OverwriteChanges , el objeto se actualiza antes de que se devuelva. Para obtener más información, consulte Resolución de identidades, Administración de estado y Seguimiento de cambios. |
Materialización de los objetos | Moderado3 | Una vez para cada objeto que devuelve una consulta. 4 | El proceso de leer el objeto devuelto DbDataReader y crear objetos y establecer valores de propiedad basados en los valores de cada instancia de la DbDataRecord clase. Si el objeto ya existe en ObjectContext y la consulta usa las AppendOnly opciones de combinación o PreserveChanges , esta fase no afecta al rendimiento. Para obtener más información, consulte Resolución de identidades, Administración de estado y Seguimiento de cambios. |
1 Cuando un proveedor de orígenes de datos implementa la agrupación de conexiones, el costo de abrir una conexión se distribuye a través del grupo. El proveedor de .NET para SQL Server admite la agrupación de conexiones.
2 El costo aumenta con una mayor complejidad de las consultas.
3 El costo total aumenta proporcionalmente al número de objetos devueltos por la consulta.
4 Esta sobrecarga no es necesaria para las consultas EntityClient porque las consultas EntityClient devuelven un EntityDataReader en lugar de objetos. Para obtener más información, consulte 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 pueden ser intensivas en recursos, tenga en cuenta en qué punto del código y en qué equipo se ejecuta una consulta.
Ejecución diferida frente a ejecución inmediata
Al crear una ObjectQuery<T> consulta o LINQ, es posible que la consulta no se ejecute inmediatamente. La ejecución de consultas se aplaza hasta que se necesitan los resultados, como durante una enumeración en C# o Visual Basic, o cuando se asigna para llenar una colección. La ejecución de consultas inicia inmediatamente cuando se llama a un método Execute en un ObjectQuery<T> 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 objetos y ejecución de consultas (LINQ to Entities).
Ejecución de consultas LINQ en el lado del cliente
Aunque la ejecución de una consulta LINQ se produce en el equipo que hospeda el origen de datos, algunas partes de una consulta LINQ 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 consulta y mapeo
La complejidad de las consultas individuales y del mapeo en el modelo de entidad tendrán un efecto significativo en el rendimiento de las consultas.
Complejidad del mapeo
Los modelos más complejos que una asignación uno a uno simple entre entidades del modelo conceptual y las tablas del modelo de almacenamiento generan comandos más complejos que los modelos que tienen una asignación uno a uno.
Complejidad de las consultas
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 en un modelo conceptual que parezcan simples pueden dar lugar a la ejecución de consultas más complejas en el origen de datos. Esto puede ocurrir 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 del modelo conceptual se asigna a más de una tabla del origen de datos o cuando se asigna una relación entre entidades a una tabla de combinación, el comando de consulta ejecutado en la consulta del origen de datos puede requerir una o varias combinaciones.
Nota:
Use el método ToTraceString de las clases ObjectQuery<T> o EntityCommand para ver los comandos que se ejecutan en el origen de datos para una consulta determinada. Para obtener más información, vea How to: View the Store Commands.
Las consultas anidadas de Entity SQL pueden crear combinaciones en el servidor y pueden 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, estas consultas hacen que la canalización de consultas genere una sola consulta con duplicación de objetos entre consultas anidadas. Debido a esto, una sola columna se puede duplicar varias veces. En algunas bases de datos, incluido SQL Server, esto puede hacer que la tabla TempDB crezca muy grande, lo que puede reducir el rendimiento del servidor. Conviene tener cuidado al ejecutar consultas anidadas.
Las consultas que devuelven una gran cantidad de datos pueden provocar una disminución del rendimiento si el cliente está realizando operaciones que consumen recursos de una manera proporcional al tamaño del conjunto de resultados. En tales casos, debe considerar la posibilidad de limitar la cantidad de datos devueltos por la consulta. Para obtener más información, vea Cómo: Navegar a través de los resultados de las consultas.
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 relaciones entre entidades como asociaciones en el modelo de entidad y como relaciones lógicas en el origen de datos.
Rutas de la consulta
De forma predeterminada, cuando ejecutas un ObjectQuery<T>, no se devuelven los objetos relacionados (aunque los objetos que representan las relaciones en sí mismas sí se devuelven). Puede cargar objetos relacionados de una de estas tres maneras:
Establezca la ruta de acceso de la consulta antes de que se ejecute ObjectQuery<T>.
Llame al método
Load
sobre la propiedad de navegación expuesta por el objeto.Establezca la opción LazyLoadingEnabled en ObjectContext a
true
. Tenga en cuenta que esto se realiza automáticamente al generar código de capa de objeto con el Diseñador de modelos de datos de entidad. Para obtener más información, consulte Introducción al código generado.
Cuando tenga en cuenta qué opción usar, tenga en cuenta que hay un equilibrio entre el número de solicitudes en la base de datos y la cantidad de datos devueltos en una sola consulta. Para obtener más información, vea Cargar objetos relacionados.
Uso de rutas de consulta
Las rutas de acceso de consulta definen el gráfico de objetos que devuelve una consulta. Cuando se define una ruta de acceso de consulta, solo se requiere una solicitud única en la base de datos para devolver todos los objetos que define la ruta de acceso. 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 ocurre porque se requieren una o varias combinaciones para devolver 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:
Utilice el método ToTraceString para ver el comando que será generado por un ObjectQuery<T>. Para obtener más información, vea How to: View the Store Commands.
Cuando una ruta de acceso de consulta incluye demasiados objetos relacionados o los objetos contienen demasiados datos de fila, es posible que el origen de datos no pueda completar la consulta. Esto ocurre si la consulta requiere almacenamiento temporal intermedio que supera las funcionalidades del origen de datos. Cuando esto ocurre, puede reducir la complejidad de la consulta del origen de datos cargando explícitamente objetos relacionados.
Carga explícita de objetos relacionados
Puede cargar explícitamente objetos relacionados llamando al método Load
en una propiedad de navegación que devuelve un EntityCollection<TEntity> o un EntityReference<TEntity>. La carga explícita de objetos requiere un recorrido de ida y vuelta a la base de datos cada vez que se llama a 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. Para una base de datos de SQL Server, debe especificar un valor de MultipleActiveResultSets = true
en la cadena de conexión del proveedor.
También puede usar el LoadProperty método cuando no hay propiedades EntityCollection<TEntity> o EntityReference<TEntity> en entidades. Esto resulta útil cuando se usan entidades POCO.
Aunque la carga explícita de objetos relacionados reducirá el número de combinaciones y reducirá la cantidad de datos redundantes, Load
requiere conexiones repetidas a la base de datos, lo que puede resultar costoso al cargar explícitamente un gran número de objetos.
Guardar cambios
Cuando llamas al método SaveChanges en 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 sola transacción. 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 en una transacción explícita que requieren recursos administrados por el coordinador de transacciones distribuidas (DTC) serán mucho más costosos que una operación similar que no requiera el 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.
Transacción explícita con una operación contra SQL Server 2005 cuando Entity Framework administra la conexión. Esto ocurre porque SQL Server 2005 promueve una transacción DTC cada vez que se cierra una conexión y se vuelve a abrir dentro de una única transacción, lo cual es el comportamiento predeterminado de Entity Framework. Esta promoción de DTC no se produce al usar SQL Server 2008. Para evitar esta promoción al usar SQL Server 2005, debe abrir y cerrar explícitamente la conexión dentro de la transacción. Para obtener más información, consulte Administración de conexiones y transacciones.
Se usa una transacción explícita cuando se ejecutan una o varias operaciones dentro de una System.Transactions transacción. Para obtener más información, consulte Administración de conexiones y transacciones.
Estrategias para mejorar el rendimiento
Puede mejorar el rendimiento general de las consultas en Entity Framework mediante las estrategias siguientes.
Generar previamente las vistas
La generación de vistas basadas en un modelo de entidad es un costo significativo la primera vez que una aplicación ejecuta una consulta. Use la utilidad EdmGen.exe para generar previamente vistas como un archivo de código de Visual Basic o C# que se puede agregar al proyecto durante el diseño. También puede usar Text Template Transformation Toolkit para generar vistas precompiladas. Las vistas generadas previamente se validan en tiempo de ejecución para asegurarse de que son coherentes con la versión actual del modelo de entidad especificado. Para obtener más información, consulte Cómo: Generar vistas previamente para mejorar el rendimiento de las consultas.
Al trabajar con modelos muy grandes, se aplica la siguiente consideración:
El formato de metadatos de .NET limita el número de caracteres de cadena de usuario de un binario determinado a 16 777 215 (0xFFFFFF). Si va a generar vistas para un modelo muy grande y el archivo de vista alcanza este límite de tamaño, obtendrá el error de compilación "No queda espacio lógico para crear más cadenas de usuario". Esta limitación de tamaño se aplica a todos los archivos binarios administrados. Para obtener más información, consulte el blog que muestra cómo evitar el error al trabajar con modelos grandes y complejos.
Considerar la posibilidad de usar la opción de fusión mediante combinación NoTracking en las consultas
Hay un costo necesario para realizar un seguimiento de los objetos devueltos en el contexto del objeto. La detección de cambios en los objetos y la garantía de que varias solicitudes para la misma entidad lógica devuelven la misma instancia de objeto requiere que los objetos se conecten a una ObjectContext instancia. Si no tiene previsto realizar actualizaciones o eliminaciones en objetos y no requiere administración de identidades, considere la posibilidad de usar las NoTracking opciones de combinación al ejecutar consultas.
Devolver la cantidad correcta de datos
En algunos escenarios, especificar una ruta de acceso de consulta mediante el Include método 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 de ida y vuelta adicionales a la base de datos para cargar objetos relacionados pueden ser más rápidos porque las consultas más sencillas con menos combinaciones dan lugar a una menor redundancia de los datos. Por este motivo, se recomienda probar el rendimiento de varias maneras de recuperar objetos relacionados. Para obtener más información, vea Cargar objetos relacionados.
Para evitar devolver demasiados datos en una sola consulta, considere la posibilidad de paginar los resultados de la consulta en grupos más fáciles de administrar. Para obtener más información, vea Cómo: Navegar a través de los resultados de las consultas.
Limitar el ámbito de ObjectContext
En la mayoría de los casos, debería crear una instancia de ObjectContext dentro de una instrucción using
(Using…End Using
en Visual Basic). Esto puede aumentar el rendimiento asegurándose de 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 están enlazados a objetos administrados por el contexto del objeto, la ObjectContext instancia debe mantenerse siempre que se necesite el enlace y se elimine manualmente. Para obtener más información, consulte Administración de conexiones y transacciones.
Considere la posibilidad de abrir la conexión de base de datos manualmente
Cuando la aplicación ejecuta una serie de consultas de objetos o llama con frecuencia a SaveChanges para persistir 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 estas situaciones, considere la posibilidad de abrir manualmente la conexión al principio de estas operaciones y cerrar o eliminar la conexión cuando se completen las operaciones. Para obtener más información, consulte Administración de conexiones y transacciones.
Datos de rendimiento
Algunos datos de rendimiento de Entity Framework se publican en las siguientes entradas del blog del equipo de ADO.NET:
Exploración del rendimiento de Entity Framework de ADO.NET: parte 1
Exploración del rendimiento de Entity Framework de ADO.NET: parte 2