Siéntete libre de compartir nuestra página y códigos en tus redes, danos de tu ayuda para aumentar esta comunidad.
Solución:
A su comentario en los comentarios a su pregunta:
“…Guardando cambios (para cada registro) … “
¡Eso es lo peor que puedes hacer! Vocación SaveChanges()
para cada registro ralentiza extremadamente las inserciones masivas. Haría algunas pruebas simples que muy probablemente mejorarán el rendimiento:
- Llamar
SaveChanges()
una vez después de TODOS los registros. - Llamar
SaveChanges()
después de, por ejemplo, 100 registros. - Llamar
SaveChanges()
después de, por ejemplo, 100 registros y elimine el contexto y cree uno nuevo. - Deshabilitar la detección de cambios
Para inserciones masivas, estoy trabajando y experimentando con un patrón como este:
using (TransactionScope scope = new TransactionScope())
MyDbContext context = null;
try
context = new MyDbContext();
context.Configuration.AutoDetectChangesEnabled = false;
int count = 0;
foreach (var entityToInsert in someCollectionOfEntitiesToInsert)
++count;
context = AddToContext(context, entityToInsert, count, 100, true);
context.SaveChanges();
finally
if (context != null)
context.Dispose();
scope.Complete();
private MyDbContext AddToContext(MyDbContext context,
Entity entity, int count, int commitCount, bool recreateContext)
context.Set().Add(entity);
if (count % commitCount == 0)
context.SaveChanges();
if (recreateContext)
context.Dispose();
context = new MyDbContext();
context.Configuration.AutoDetectChangesEnabled = false;
return context;
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 desempeño 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 adjuntas al contexto en el estado Unchanged
. 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 560000 entidades:
- commitCount = 1, recreateContext = false: muchas horas (Ese es su procedimiento actual)
- commitCount = 100, recreateContext = false: más de 20 minutos
- commitCount = 1000, recreateContext = false: 242 segundos
- commitCount = 10000, recreateContext = false: 202 segundos
- commitCount = 100000, recreateContext = false: 199 segundos
- commitCount = 1000000, recreateContext = false: excepción de memoria insuficiente
- commitCount = 1, recreateContext = true: más de 10 minutos
- commitCount = 10, recreateContext = true: 241 segundos
- commitCount = 100, recreateContext = true: 164 segundos
- commitCount = 1000, recreateContext = true: 191 segundos
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 demás pruebas.
Esta combinación aumenta la velocidad bastante bien.
context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;
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 usar la inserción normal o AddRange
el uso es extremadamente simple
context.BulkInsert(hugeAmountOfEntities);
Reseñas y valoraciones
Si para ti ha resultado provechoso nuestro artículo, sería de mucha ayuda si lo compartieras con más seniors de esta forma contrubuyes a extender este contenido.