2010-12-04 5 views
26

Tengo que importar alrededor de 30k filas desde un archivo CSV a mi base de datos SQL, esto lamentablemente lleva 20 minutos.¿Cómo acelero DbSet.Add()?

La solución de problemas con un generador de perfiles me muestra que DbSet.Add toma más tiempo, pero ¿por qué?

que tienen estos Entity Framework Código-Primeras clases:

public class Article 
{ 
    // About 20 properties, each property doesn't store excessive amounts of data 
} 

public class Database : DbContext 
{ 
    public DbSet<Article> Articles { get; set; } 
} 

para cada elemento de mi bucle que hago:

db.Articles.Add(article); 

Fuera del bucle for que hago:

db.SaveChanges(); 

Está conectado con mi servidor SQLExpress local, pero supongo que no hay nada escrito hasta Sav Se llama eChanges así que supongo que el servidor no será el problema ...

+1

Hola. ¿Se deshizo de Entity Framework o usó sqlbulkcopy junto con EF? Estoy obteniendo exactamente el mismo problema con .Add() –

+6

Si configura esto: 'db.Configuration.ValidateOnSaveEnabled = false; db.Configuration.AutoDetectChangesEnabled = false; ' Hay una gran ganancia de rendimiento. Debe estar seguro de sus valores difíciles. –

+0

Usa barras (') para el código en los comentarios. Parece interesante, investigaré esas propiedades más adelante ... –

Respuesta

8

Cada artículo en una unidad de trabajo tiene una sobrecarga, ya que debe verificar (y actualizar) el administrador de identidades, agregar a varios colecciones, etc.

Lo primero que intentaría es agrupar, por ejemplo, grupos de 500 (cambie ese número según corresponda), comenzando con un contexto de objetos nuevo (nuevo) cada vez, de lo contrario, puede esperar razonablemente rendimiento telescópico. Romperlo en lotes también evita una transacción megalítica que detiene todo.

Más allá de eso; SqlBulkCopy. Está diseñado para grandes importaciones con una sobrecarga mínima. No es EF sin embargo.

+0

+1 si corresponde en su diseño Definitivamente iría con SqlBulkCopy. –

+0

Estoy tratando de lograr algo con esto ahora, pero me pregunto si solo aceptará la coincidencia basada en los nombres de las columnas y no en sus propiedades ... –

+2

La sugerencia de los grupos hizo que sea un poco más rápido pero no funcionó rápido suficiente. Después de algunas iteraciones a través de errores desagradables, conseguí que SqlBulkCopy funcionara, aunque es un código desagradable, pero funciona. Podría refactorizarlo o verificar si tienen soporte para la inserción masiva más tarde ... ¡Gracias Marc y las personas en el chat que hicieron una sugerencia similar! Y mira, algo que tomó ** 20 minutos ** ahora toma ** 2 segundos **, es mágico ... –

1

Realmente no he intentado esto, pero mi lógica sería aferrarme al controlador ODBC para cargar el archivo en datatable y luego usar el procedimiento almacenado sql para pasar la tabla al procedimiento.

Para la primera parte, trata de: http://www.c-sharpcorner.com/UploadFile/mahesh/AccessTextDb12052005071306AM/AccessTextDb.aspx

Para la segunda parte probar esto por procedimiento SQL: http://www.builderau.com.au/program/sqlserver/soa/Passing-table-valued-parameters-in-SQL-Server-2008/0,339028455,339282577,00.htm

Y crear el objeto SqlCommnand en C# y añadir a su SqlParameter colección de parámetros que es SqlDbType. Estructurado

Bueno, espero que ayude.

43

De acuerdo con el comentario de Kevin Ramen (mar 29) puedo confirmar que la fijación de db.Configuration.AutoDetectChangesEnabled = false hace una gran diferencia en la velocidad

Correr Add() en 2324 elementos por defecto corrió 15 segundos a 3 minutos en mi máquina, la desactivación de la detección automática resultó en la operación que completa en 0.5sec.

http://blog.larud.net/archive/2011/07/12/bulk-load-items-to-a-ef-4-1-code-first-aspx

+0

Interesante ... :) –

+0

¡Esto es algo increíble de saber! Se arregló un gran problema que tuve al insertar 4k registros usando EF sin rehacer mi código para usar la copia masiva. Creo que la copia masiva es una respuesta fácil que las personas hacen sin analizar más el problema. En mi caso, el sql-insert tomaba <1s y el EF add llevaba 30-40 segundos, por lo que esta solución funciona perfectamente. ¡Gracias por la información! – Alex

16

Voy a añadir al comentario de Kervin Ramen diciendo que si sólo se está haciendo inserciones (no hay actualizaciones o eliminaciones) entonces se puede, en general, fijar con seguridad las siguientes propiedades antes de hacer cualquier inserto en el contexto:

DbContext.Configuration.AutoDetectChangesEnabled = false; 
DbContext.Configuration.ValidateOnSaveEnabled = false; 

Estaba teniendo un problema con una importación a granel única en mi trabajo. Sin establecer las propiedades anteriores, agregar unos 7500 objetos complicados al contexto llevaba más de 30 minutos.Establecer las propiedades anteriores (por lo tanto, deshabilitar las comprobaciones de EF y cambiar el seguimiento) redujo la importación a segundos.

Pero, una vez más, insisto solo use esto si está haciendo inserciones. Si necesita mezclar inserciones con actualizaciones/eliminaciones, puede dividir el código en dos rutas y desactivar las comprobaciones EF para la parte insertada y luego volver a habilitar las comprobaciones para la ruta de actualización/eliminación. He utilizado este enfoque con éxito para evitar el comportamiento lento de DbSet.Add().

+0

Esto es ciertamente sorprendente y podría probarlo y compararlo con inserciones en bloque. ¡Gracias por compartir! También gracias por recordar, parece que me olvidé de ese comentario, pero lo veré mañana por la noche con seguridad ... –

+0

Al intentarlo, esto parece más lento que las inserciones masivas, así que no puedo usar este enfoque. En detalle, estoy haciendo 350.000 '.Add()' s (entidades sin referencias a otras entidades, solo campos con valores razonables) seguido de '.SaveChanges()', configurándolos en falso antes de llamar a adds o guardar cambios y volver a verdadero después de guardar los cambios; toma mucho más tiempo que las inserciones a granel, así que ni siquiera me molesto en dejarlo funcionando. –

+0

No puedo creerlo.Esto hizo mi día y mi jefe será feliz. Funciona como un encanto :) – Alireza

4

No es una herramienta extremadamente fácil de usar y de extensión muy rápido aquí: https://efbulkinsert.codeplex.com/

Se llama "Marco de la entidad inserción masiva".

La extensión está en el espacio de nombres EntityFramework.BulkInsert.Extensions. Así que para revelar el método de extensión añadir usando

using EntityFramework.BulkInsert.Extensions; 

Y entonces usted puede hacer esto

context.BulkInsert(entities); 

BTW - Si no desea utilizar esta extensión, por alguna razón, también se podría tratar en lugar de correr db.Articles.Add (artículo) para cada artículo, para crear cada vez una lista de varios artículos y luego use AddRange (nuevo en EF versión 6, junto con RemoveRange) para agregarlos al dbcontext.

+0

¿Qué hace para mejorar el rendimiento? – PeterX

+0

Lamentablemente, aparece el mensaje 'La clave dada no estaba presente en el diccionario' y parece que no hay una gran respuesta en [aquí] (http://stackoverflow.com/a/26427216/845584) – PeterX

+0

Se saltea la validación llama a cada fila, haciendo una validación al final – ScottB

Cuestiones relacionadas