Estoy buscando la forma más rápida de insertar en Entity Framework.
Pregunto esto debido al escenario en el que tiene un TransactionScope activo y la inserción es enorme (4000+). Potencialmente puede durar más de 10 minutos (tiempo de espera predeterminado de las transacciones), y esto conducirá a una transacción incompleta.
c#
sql
entity-framework
Bongo Sharp
fuente
fuente
Respuestas:
Para su comentario en los comentarios a su pregunta:
¡Eso es lo peor que puedes hacer! La solicitud
SaveChanges()
de cada registro ralentiza las inserciones masivas extremadamente bajas. Haría algunas pruebas simples que muy probablemente mejorarán el rendimiento:SaveChanges()
una vez después de TODOS los registros.SaveChanges()
después, por ejemplo, 100 registros.SaveChanges()
después de, por ejemplo, 100 registros y elimine el contexto y cree uno nuevo.Para inserciones masivas, estoy trabajando y experimentando con un patrón como este:
Tengo un programa de prueba que inserta 560,000 entidades (9 propiedades escalares, sin propiedades de navegación) en la base de datos. Con este código funciona en menos de 3 minutos.
Para el rendimiento es importante llamar
SaveChanges()
después de "muchos" registros ("muchos" alrededor de 100 o 1000). También mejora el rendimiento para eliminar el contexto después de SaveChanges y crear uno nuevo. Esto borra el contexto de todas las entidades,SaveChanges
no hace eso, las entidades todavía están unidas al contexto en estadoUnchanged
. Es el tamaño creciente de las entidades adjuntas en el contexto lo que ralentiza la inserción paso a paso. Por lo tanto, es útil borrarlo después de un tiempo.Aquí hay algunas medidas para mis entidades 560000:
El comportamiento en la primera prueba anterior es que el rendimiento es muy no lineal y disminuye extremadamente con el tiempo. ("Muchas horas" es una estimación, nunca terminé esta prueba, me detuve en 50,000 entidades después de 20 minutos). Este comportamiento no lineal no es tan significativo en todas las otras pruebas.
fuente
AutoDetectChangesEnabled = false;
el DbContext. También tiene un gran efecto de rendimiento adicional: stackoverflow.com/questions/5943394/…DbContext
NOObjectContext
?Esta combinación aumenta la velocidad lo suficientemente bien.
fuente
La forma más rápida sería usar la extensión de inserción masiva , que desarrollé
nota: este es un producto comercial, no gratuito
Utiliza SqlBulkCopy y un lector de datos personalizado para obtener el máximo rendimiento. Como resultado, es más de 20 veces más rápido que el uso de inserción regular o AddRange
el uso es extremadamente simple
fuente
Deberías mirar el uso
System.Data.SqlClient.SqlBulkCopy
de esto. Aquí está la documentación y, por supuesto, hay muchos tutoriales en línea.Lo siento, sé que estaba buscando una respuesta simple para que EF haga lo que desea, pero las operaciones masivas no son realmente para lo que están destinados los ORM.
fuente
Estoy de acuerdo con Adam Rackis.
SqlBulkCopy
es la forma más rápida de transferir registros masivos de una fuente de datos a otra. Utilicé esto para copiar 20K registros y me tomó menos de 3 segundos. Echa un vistazo al siguiente ejemplo.fuente
AsDataReader()
método de extensión, explicado en esta respuesta: stackoverflow.com/a/36817205/1507899Recomendaría este artículo sobre cómo hacer inserciones masivas usando EF.
Entity Framework e INSERTs masivos lentos
Explora estas áreas y compara el rendimiento:
fuente
como nunca se mencionó aquí, quiero recomendar EFCore.BulkExtensions aquí
fuente
Investigué la respuesta de Slauma (que es increíble, gracias por la idea, hombre), y reduje el tamaño del lote hasta alcanzar la velocidad óptima. Mirando los resultados de Slauma:
Es visible que hay un aumento de velocidad cuando se mueve de 1 a 10, y de 10 a 100, pero de 100 a 1000 la velocidad de inserción vuelve a caer.
Por lo tanto, me he centrado en lo que sucede cuando reduce el tamaño del lote a un valor entre 10 y 100, y aquí están mis resultados (estoy usando diferentes contenidos de fila, por lo que mis tiempos son de diferente valor):
Según mis resultados, el valor óptimo real es de alrededor de 30 para el tamaño del lote. Es menor que 10 y 100. El problema es que no tengo idea de por qué es 30 óptimo, ni podría haber encontrado ninguna explicación lógica para ello.
fuente
Como otras personas han dicho, SqlBulkCopy es la forma de hacerlo si desea un rendimiento de inserción realmente bueno.
Implementarlo es un poco engorroso, pero hay bibliotecas que pueden ayudarlo. Hay algunos por ahí, pero descaradamente conectaré mi propia biblioteca esta vez: https://github.com/MikaelEliasson/EntityFramework.Utilities#batch-insert-entities
El único código que necesitarías es:
Entonces, ¿cuánto más rápido es? Muy difícil de decir porque depende de muchos factores, el rendimiento de la computadora, la red, el tamaño del objeto, etc. Las pruebas de rendimiento que he realizado sugieren que se pueden insertar 25k entidades en aproximadamente 10 segundos de la manera estándar en localhost SI optimizas tu configuración de EF como mencionado en las otras respuestas. Con EFUtilities que dura unos 300 ms. Aún más interesante es que he ahorrado alrededor de 3 millones de entidades en menos de 15 segundos usando este método, promediando alrededor de 200k entidades por segundo.
El único problema es, por supuesto, si necesita insertar datos relegados. Esto se puede hacer de manera eficiente en el servidor SQL usando el método anterior, pero requiere que tenga una estrategia de generación de Id que le permita generar identificadores en el código de la aplicación para el padre para que pueda configurar las claves externas. Esto se puede hacer usando GUID o algo así como la generación de id de HiLo.
fuente
EFBatchOperation
tuviera un constructor al que le pase enDbContext
lugar de pasar a cada método estático. Las versiones genéricas deInsertAll
yUpdateAll
que encuentran automáticamente la colección, similar aDbContext.Set<T>
, también serían buenas.Dispose()
el contexto crea problemas si las entidades en las queAdd()
confía en otras entidades precargadas (por ejemplo, propiedades de navegación) en el contextoUtilizo un concepto similar para mantener mi contexto pequeño para lograr el mismo rendimiento
Pero en lugar del
Dispose()
contexto y recrear, simplemente separo las entidades que yaSaveChanges()
envuélvelo con try catch y
TrasactionScope()
si lo necesitas, no los muestres aquí para mantener el código limpiofuente
Sé que esta es una pregunta muy antigua, pero un tipo aquí dijo que desarrolló un método de extensión para usar inserción masiva con EF, y cuando lo comprobé, descubrí que la biblioteca cuesta $ 599 hoy (para un desarrollador). Tal vez tenga sentido para toda la biblioteca, sin embargo, solo para la inserción masiva, esto es demasiado.
Aquí hay un método de extensión muy simple que hice. Lo uso primero en pareja con la base de datos (no lo pruebo primero con el código, pero creo que funciona igual). Cambiar
YourEntities
con el nombre de su contexto:Puede usar eso contra cualquier colección que herede de
IEnumerable
, así:fuente
await bulkCopy.WriteToServerAsync(table);
Intente utilizar un Procedimiento almacenado que obtenga un XML de los datos que desea insertar.
fuente
He hecho una extensión genérica del ejemplo anterior de @Slauma;
Uso:
fuente
Hay algunas bibliotecas de terceros que admiten Bulk Insert disponibles:
Ver: biblioteca de inserción masiva de Entity Framework
Tenga cuidado al elegir una biblioteca de inserción masiva. Solo Entity Framework Extensions admite todo tipo de asociaciones y herencias y es el único que aún se admite.
Descargo de responsabilidad : soy el propietario de Entity Framework Extensions
Esta biblioteca le permite realizar todas las operaciones masivas que necesita para sus escenarios:
Ejemplo
fuente
Uso
SqlBulkCopy
:fuente
Una de las formas más rápidas de guardar una lista debe aplicar el siguiente código
AutoDetectChangesEnabled = false
Agregar, agregar rango y guardar cambios: no detecta cambios.
ValidateOnSaveEnabled = false;
No detecta el rastreador de cambios
Debes agregar nuget
Ahora puedes usar el siguiente código
fuente
SqlBulkCopy es súper rápido
Esta es mi implementación:
fuente
[Actualización 2019] EF Core 3.1
Siguiendo lo que se dijo anteriormente, deshabilitar AutoDetectChangesEnabled en EF Core funcionó perfectamente: el tiempo de inserción se dividió entre 100 (de muchos minutos a unos segundos, 10k registros con relaciones de tablas cruzadas)
El código actualizado es:
fuente
Aquí hay una comparación de rendimiento entre el uso de Entity Framework y el uso de la clase SqlBulkCopy en un ejemplo realista: Cómo insertar en masa objetos complejos en la base de datos de SQL Server
Como otros ya enfatizaron, los ORM no están destinados a ser utilizados en operaciones masivas. Ofrecen flexibilidad, separación de preocupaciones y otros beneficios, pero las operaciones masivas (excepto la lectura masiva) no son una de ellas.
fuente
Otra opción es usar SqlBulkTools disponible de Nuget. Es muy fácil de usar y tiene algunas características poderosas.
Ejemplo:
Consulte la documentación para obtener más ejemplos y uso avanzado. Descargo de responsabilidad: soy el autor de esta biblioteca y cualquier opinión es de mi propia opinión.
fuente
Según mi conocimiento no es
no BulkInsert
enEntityFramework
aumentar el rendimiento de las enormes inserciones.En este escenario se puede ir con SqlBulkCopy en
ADO.net
resolver su problemafuente
WriteToServer
que requiere unDataTable
.¿Alguna vez ha tratado de insertar a través de un trabajador o tarea en segundo plano?
En mi caso, estoy insertando 7760 registros, distribuidos en 182 tablas diferentes con relaciones de clave externa (por NavigationProperties).
Sin la tarea, tomó 2 minutos y medio. Dentro de una tarea (
Task.Factory.StartNew(...)
), tardó 15 segundos.Solo estoy haciendo lo siguiente
SaveChanges()
después de agregar todas las entidades al contexto. (para garantizar la integridad de los datos)fuente
Todas las soluciones escritas aquí no ayudan porque cuando hace SaveChanges (), las instrucciones de inserción se envían a la base de datos una por una, así es como funciona la entidad.
Y si su viaje a la base de datos y de regreso es de 50 ms, por ejemplo, el tiempo necesario para la inserción es el número de registros x 50 ms.
Tienes que usar BulkInsert, aquí está el enlace: https://efbulkinsert.codeplex.com/
El tiempo de inserción se redujo de 5-6 minutos a 10-12 segundos al usarlo.
fuente
Puede usar la biblioteca de paquetes masivos. La versión Bulk Insert 1.0.0 se usa en proyectos que tienen Entity framework> = 6.0.0.
Se puede encontrar más descripción aquí : código fuente de Bulkoperation
fuente
[NUEVA SOLUCIÓN PARA POSTGRESQL] Hola, sé que es una publicación bastante antigua, pero recientemente me he encontrado con un problema similar, pero estábamos usando Postgresql. Quería usar Bulkinsert efectivo, lo que resultó ser bastante difícil. No he encontrado ninguna biblioteca gratuita adecuada para hacerlo en este DB. Solo he encontrado este ayudante: https://bytefish.de/blog/postgresql_bulk_insert/ que también está en Nuget. He escrito un pequeño mapeador, que asigna automáticamente propiedades de la forma en que Entity Framework:
Lo uso de la siguiente manera (tenía una entidad llamada Undertaking):
Mostré un ejemplo con la transacción, pero también se puede hacer con una conexión normal recuperada del contexto. enterprisesToAdd es enumerable de registros de entidades normales, que quiero insertar a granel en DB.
¡Esta solución, que obtuve después de algunas horas de investigación y prueba, es como podría esperarse mucho más rápido y finalmente fácil de usar y gratis! Realmente te recomiendo que uses esta solución, no solo por las razones mencionadas anteriormente, sino también porque es la única con la que no tuve problemas con Postgresql, muchas otras soluciones funcionan perfectamente, por ejemplo con SqlServer.
fuente
El secreto es insertar en una tabla de preparación en blanco idéntica. Los insertos se aclaran rápidamente. Luego, ejecute un único inserto desde ese punto en su tabla grande principal. Luego truncar la tabla de preparación lista para el próximo lote.
es decir.
fuente
Pero, para más de (+4000) inserciones, recomiendo usar el procedimiento almacenado. adjunto el tiempo transcurrido. Lo inserté 11.788 filas en 20 "
eso es código
fuente
Utilice el procedimiento almacenado que toma datos de entrada en forma de xml para insertar datos.
Desde su pase de código C # inserte datos como xml
Por ejemplo, en C #, la sintaxis sería así:
fuente
Use esta técnica para aumentar la velocidad de inserción de registros en Entity Framework. Aquí utilizo un procedimiento almacenado simple para insertar los registros. Y para ejecutar este procedimiento almacenado, uso el método .FromSql () de Entity Framework que ejecuta SQL sin formato.
El código de procedimiento almacenado:
Luego, recorra todos sus 4000 registros y agregue el código de Entity Framework que ejecuta el almacenado
El procedimiento se realiza cada 100 ciclos.
Para esto creo una consulta de cadena para ejecutar este procedimiento, continúo agregando cada conjunto de registros.
Luego verifique que el ciclo se esté ejecutando en múltiplos de 100 y, en ese caso, ejecútelo usando
.FromSql()
.Verifique el siguiente código:
fuente