Entity Framework es muy lento. ¿Cuáles son mis opciones?

Seguí el mantra “No optimizar prematuramente” y codifiqué mi Servicio WCF utilizando Entity Framework.

Sin embargo, perfilé el rendimiento y Entity Framework es demasiado lento. (Mi aplicación procesa 2 mensajes en aproximadamente 1.2 segundos, donde la aplicación (heredada) que estoy reescribiendo recibe entre 5 y 6 mensajes al mismo tiempo. (La aplicación heredada llama a sprocs para su acceso de base de datos).

Mi perfil apunta a Entity Framework tomando la mayor parte del tiempo por mensaje.

¿Entonces cuales son mis opciones?

  • ¿Hay mejores ORM por ahí?
    (Algo que solo admite la lectura y escritura normales de objetos y lo hace rápido ..)

  • ¿Hay alguna manera de hacer que Entity Framework sea más rápido?
    ( Nota : cuando digo más rápido me refiero a largo plazo, no a la primera llamada. (La primera llamada es lenta (15 segundos para un mensaje), pero eso no es un problema. Solo necesito que sea rápido para el rest de los mensajes.)

  • Alguna misteriosa tercera opción que me ayudará a obtener más velocidad de mi servicio.

NOTA: la mayoría de las interacciones de mi base de datos son Crear y Actualizar. Hago muy pocas selecciones y eliminaciones.

Debería comenzar por perfilar los comandos SQL realmente emitidos por Entity Framework. Dependiendo de su configuración (POCO, entidades de seguimiento automático) hay mucho espacio para optimizaciones. Puede depurar los comandos SQL (que no deberían diferir entre el modo de depuración y el de liberación) utilizando el ObjectSet.ToTraceString() . Si encuentra una consulta que requiera una mayor optimización, puede usar algunas proyecciones para darle a EF más información sobre lo que intenta lograr.

Ejemplo:

 Product product = db.Products.SingleOrDefault(p => p.Id == 10); // executes SELECT * FROM Products WHERE Id = 10 ProductDto dto = new ProductDto(); foreach (Category category in product.Categories) // executes SELECT * FROM Categories WHERE ProductId = 10 { dto.Categories.Add(new CategoryDto { Name = category.Name }); } 

Podría ser reemplazado por:

 var query = from p in db.Products where p.Id == 10 select new { p.Name, Categories = from c in p.Categories select c.Name }; ProductDto dto = new ProductDto(); foreach (var categoryName in query.Single().Categories) // Executes SELECT p.Id, c.Name FROM Products as p, Categories as c WHERE p.Id = 10 AND p.Id = c.ProductId { dto.Categories.Add(new CategoryDto { Name = categoryName }); } 

Acabo de tipear eso de mi cabeza, así que esta no es exactamente la forma en que se ejecutará, pero EF realmente hace algunas buenas optimizaciones si le dices todo lo que sabes sobre la consulta (en este caso, que necesitaremos la categoría- nombres). Pero esto no es como la carga ansiosa (db.Products.Include (“Categorías”)) porque las proyecciones pueden reducir aún más la cantidad de datos a cargar.

El hecho es que productos como Entity Framework SIEMPRE serán lentos e ineficientes, ya que están ejecutando mucho más código.

También me parece tonto que la gente sugiera que uno debe optimizar las consultas LINQ, ver el SQL generado, usar depuradores, precomstackr, tomar muchos pasos adicionales, etc., es decir, perder mucho tiempo. Nadie dice – ¡Simplifica! Todo el mundo quiere complicar aún más las cosas dando más pasos (perdiendo el tiempo).

Un enfoque de sentido común sería no usar EF o LINQ en absoluto. Use SQL simple. No tiene nada de malo. El hecho de que haya una mentalidad de rebaño entre los progtwigdores y que sientan la necesidad de utilizar cada uno de los productos nuevos, no significa que sea bueno o que funcione. La mayoría de los progtwigdores piensan que si incorporan cada nueva pieza de código lanzado por una gran empresa, los convierte en un progtwigdor más inteligente; no es cierto del todo. La progtwigción inteligente se trata principalmente de cómo hacer más con menos dolores de cabeza, incertidumbres y en el menor tiempo posible. ¡Tiempo de recordar! Ese es el elemento más importante, así que trate de encontrar maneras de no desperdiciarlo al resolver problemas en un código incorrecto / inflado escrito simplemente para conformarse con algunos extraños llamados ‘patrones’.

Relájate, disfruta de la vida, toma un descanso de la encoding y deja de usar funciones adicionales, códigos, productos, ‘patrones’. La vida es corta y la vida de su código es aún más corta, y ciertamente no es ciencia espacial. Elimine capas como LINQ, EF y otras, y su código se ejecutará de manera eficiente, se escalará y, sí, será fácil de mantener. Demasiada abstracción es un mal ‘patrón’.

Y esa es la solución a tu problema.

Una sugerencia es utilizar LINQ para Entity Framework solo para declaraciones CRUD de registro único.

Para consultas, búsquedas, informes, etc. más involucrados, escriba un procedimiento almacenado y agréguelo al modelo de Entity Framework como se describe en MSDN .

Este es el enfoque que he tomado con algunos de mis sitios y parece ser un buen compromiso entre productividad y rendimiento. Entity Framework no siempre generará el SQL más eficiente para la tarea en cuestión. Y en lugar de perder el tiempo para descubrir por qué, escribir un procedimiento almacenado para las consultas más complejas realmente ahorra tiempo para mí. Una vez que esté familiarizado con el proceso, no es demasiado complicado agregar procs almacenados a su modelo EF. Y, por supuesto, la ventaja de agregarlo a su modelo es que obtiene todas las bondades fuertemente tipadas que provienen del uso de un ORM.

Si solo está obteniendo datos, es una gran ayuda para el rendimiento cuando le dice a EF que no realice un seguimiento de las entidades que capta. Haga esto usando MergeOption.NoTracking. EF solo generará la consulta, la ejecutará y deserializará los resultados a los objetos, pero no intentará hacer un seguimiento de los cambios de entidad ni nada de esa naturaleza. Si una consulta es simple (no pasa mucho tiempo esperando a que la base de datos regrese), descubrí que establecerla en NoTracking puede duplicar el rendimiento de la consulta.

Vea este artículo de MSDN en la enumeración de MergeOption:

Resolución de identidad, gestión estatal y seguimiento de cambios

Este parece ser un buen artículo sobre el rendimiento de EF:

Rendimiento y el marco de la entidad

Usted dice que ha perfilado la aplicación. ¿Has perfilado el ORM también? Hay un perfilador EF de Ayende que destacará dónde puede optimizar su código EF. Lo puedes encontrar aquí:

http://efprof.com/

Recuerde que puede usar un enfoque SQL tradicional junto con su ORM si necesita obtener rendimiento.

Si hay un ORM más rápido / mejor? Dependiendo de su modelo de objeto / datos, podría considerar usar uno de los micro-ORM, como Dapper , Massive o PetaPoco .

El sitio Dapper publica algunos puntos de referencia comparativos que le darán una idea de cómo se comparan con otros ORM. Pero vale la pena señalar que los micro-ORM no son compatibles con el amplio conjunto de características de los ORM completos como EF y NH.

Es posible que desee echar un vistazo a RavenDB . Esta es una base de datos no relacional (de nuevo Ayende) que le permite almacenar POCO directamente sin necesidad de mapeo . RavenDB está optimizado para lecturas y hace que la vida de los desarrolladores sea mucho más fácil al eliminar la necesidad de manipular el esquema y asignar sus objetos a ese esquema. Sin embargo, tenga en cuenta que este es un enfoque significativamente diferente al uso de un enfoque ORM y estos se describen en el sitio del producto .

Desde mi experiencia, el problema no es con EF, sino con el enfoque ORM en sí mismo.

En general, todos los ORM sufren de N + 1 problema, consultas no optimizadas, etc. Mi mejor opción sería rastrear las consultas que causan la degradación del rendimiento y tratar de afinar la herramienta ORM, o reescribir esas partes con SPROC.

He encontrado la respuesta de @Slauma aquí muy útil para acelerar las cosas. Usé el mismo tipo de patrón para las inserciones y las actualizaciones, y el rendimiento se disparó.

Solo tiene sentido optimizar después de haber realizado un perfil. Si descubre que el acceso a la base de datos es lento, puede convertir a usar procedimientos almacenados y mantener EF. Si descubres que es la propia EF lo que es lento, es posible que tengas que cambiar a un ORM diferente o no usar un ORM en absoluto.

Esta es una opción simple sin marco, sin ORM que se carga a 10,000 / segundo con 30 campos más o menos. Correr en una computadora portátil vieja, probablemente más rápido que eso en un entorno real.

https://sourceforge.net/projects/dopersistence/?source=directory

Me encontré con este problema también. Odio descargar EF porque funciona muy bien, pero es lento. En la mayoría de los casos, solo quiero encontrar un registro o actualizar / insertar. Incluso operaciones simples como esta son lentas. Retiré 1100 registros de una tabla a una lista y esa operación duró 6 segundos con EF. Para mí esto es demasiado largo, incluso el ahorro lleva demasiado tiempo.

Terminé haciendo mi propio ORM. Saqué los mismos 1100 registros de una base de datos y mi ORM tomó 2 segundos, mucho más rápido que EF. Todo con mi ORM es casi instantáneo. La única limitación en este momento es que solo funciona con MS SQL Server, pero podría cambiarse para que funcione con otros como Oracle. Yo uso MS SQL Server para todo en este momento.

Si desea probar mi ORM aquí está el enlace y el sitio web:

https://github.com/jdemeuse1204/OR-M-Data-Entities

O si quieres usar pepita:

PM> Install-Package OR-M_DataEntities

La documentación está allí también

El Marco de la Entidad no debería causar los principales cuellos de botella. Lo más probable es que haya otras causas. Podría intentar cambiar EF a Linq2SQL, ambos tienen características comparativas y el código debería ser fácil de convertir, pero en muchos casos Linq2SQL es más rápido que EF.

Tenemos una aplicación similar (Wcf -> EF -> base de datos) que hace 120 Solicitudes por segundo fácilmente, así que estoy más que seguro de que EF no es su problema aquí, dicho esto, he visto mejoras importantes en el rendimiento con consultas comstackdas.

Utilicé EF, LINQ to SQL y dapper. Dapper es el más rápido. Ejemplo: necesitaba 1000 registros principales con 4 registros secundarios cada uno. Usé LINQ para sql, tomó aproximadamente 6 segundos. Luego cambié a apuesto, recuperé 2 juegos de registros del único procedimiento almacenado y para cada registro agregué los registros secundarios. Tiempo total 1 segundo.

También el procedimiento almacenado utilizó funciones de valores de tabla con aplicación cruzada, encontré que las funciones de valores escalares son muy lentas.

Mi consejo sería usar EF o LINQ para SQL y para ciertas situaciones cambiar a apuesto.