¿Por qué la inserción de entidades en EF 4.1 es tan lenta en comparación con ObjectContext?

81

Básicamente, inserto 35000 objetos dentro de una transacción:

using(var uow = new MyContext()){
  for(int i = 1; i < 35000; i++) {
     var o = new MyObject()...;
     uow.MySet.Add(o);
  }
  uow.SaveChanges();
}

¡Esto lleva una eternidad! Si utilizo la ObjectContext subyacente (mediante el uso IObjectAdapter), todavía es lento pero tarda alrededor de 20 segundos. Parece que DbSet<>está haciendo algunas búsquedas lineales, lo que lleva bastante tiempo ...

¿Alguien más está viendo este problema?

Hartmut
fuente
3
De alguna manera creo que la respuesta será similar a esta: stackoverflow.com/questions/5917478/…
Ladislav Mrnka

Respuestas:

128

Como ya lo indicó Ladislav en el comentario, debe deshabilitar la detección automática de cambios para mejorar el rendimiento:

context.Configuration.AutoDetectChangesEnabled = false;

Esta detección de cambios está habilitada de forma predeterminada en la DbContextAPI.

La razón por la que se DbContextcomporta de manera tan diferente de la ObjectContextAPI es que muchas más funciones de la DbContextAPI llamarán DetectChangesinternamente que funciones de la ObjectContextAPI cuando la detección automática de cambios está habilitada.

Aquí puede encontrar una lista de las funciones que llaman DetectChangespor defecto. Son:

  • Los Add, Attach, Find, Local, o Removemiembros deDbSet
  • Los GetValidationErrors, Entry, o SaveChangesmiembros deDbContext
  • El Entriesmétodo enDbChangeTracker

Especialmente las Addllamadas DetectChangesque son responsables del bajo rendimiento que experimentó.

En contraste con esto, la ObjectContextAPI llama DetectChangessolo automáticamente en SaveChangespero no en AddObjecty los otros métodos correspondientes mencionados anteriormente. Esa es la razón por la que el rendimiento predeterminado de ObjectContextes más rápido.

¿Por qué introdujeron esta detección automática de cambios predeterminada DbContexten tantas funciones? No estoy seguro, pero parece que deshabilitarlo y llamar DetectChangesmanualmente en los puntos adecuados se considera avanzado y puede introducir fácilmente errores sutiles en su aplicación, así que utilícelo con cuidado .

Slauma
fuente
@Ladislav: Tienes razón, no encontré esto porque solo estaba buscando problemas de inserción :-(
Hartmut
Gracias por la explicación. De hecho, estaba llamando a context.Configuration.AutoDetectChangesEnabled = false pero lo hice durante la construcción de la base de datos en el método Seed (). Pensé que esto establecería el valor predeterminado. No sabía que tenía que llamarlo para cada instancia. ¡Gracias!
Hartmut
3
@Hartmut: puede deshabilitar la detección de cambios dentro del constructor de su DbContext derivado, luego lo tiene siempre deshabilitado. Pero personalmente, de alguna manera, este comentario sobre "la posibilidad de introducir errores sutiles" cuando está desactivado me pone nervioso. Tengo la detección de cambios activada de forma predeterminada y la deshabilito solo en bloques de código como el suyo, donde el aumento del rendimiento es obvio y donde me siento seguro de que no causa problemas.
Slauma
Estoy de acuerdo, solo estaba probando una parte crítica de rendimiento de mi aplicación. En el código de producción, es mejor limitarlo a casos como inserciones a granel, etc.
Hartmut
Gracias por esta respuesta. Hay mucha información disponible, ¡pero esto va directo al grano!
Fred Wilson
12

Pequeña prueba empírica con EF 4.3 CodeFirst:

Se eliminaron 1000 objetos con AutoDetectChanges = true: 23 segundos

Se eliminaron 1000 objetos con AutoDetectChanges = false: 11 segundos

Se insertaron 1000 objetos con AutoDetectChanges = true: 21 segundos

Se insertaron 1000 objetos con AutoDetectChanges = false: 13 segundos

Zax
fuente
1
Gracias Zax. ¿Cuáles son sus resultados con 35.000 según la pregunta? Verá que en la pregunta original dice que el rendimiento cae cuadráticamente
Daniel Dyson
9

En .netcore 2.0 esto se movió a:

context.ChangeTracker.AutoDetectChangesEnabled = false;

Maxvt
fuente
1

Además de las respuestas que has encontrado aquí. Es importante saber que a nivel de la base de datos es más trabajo insertar que agregar. La base de datos tiene que ampliar / asignar nuevo espacio. Luego, debe actualizar al menos el índice de clave principal. Aunque los índices también se pueden actualizar al actualizar, es mucho menos común. Si hay claves foráneas, debe leer esos índices también para asegurarse de que se mantenga la integridad referencial. Los desencadenantes también pueden desempeñar un papel, aunque pueden afectar las actualizaciones de la misma manera.

Todo ese trabajo de base de datos tiene sentido en la actividad diaria de inserción originada por las entradas de los usuarios. Pero si solo está cargando una base de datos existente o tiene un proceso que genera muchas inserciones. Es posible que desee buscar formas de acelerarlo, posponiéndolo hasta el final. Normalmente, deshabilitar índices durante la inserción es una forma común. Hay optimizaciones muy complejas que se pueden hacer según el caso, pueden resultar un poco abrumadoras.

Solo sepa que, en general, la inserción llevará más tiempo que las actualizaciones.

Arturo Hernandez
fuente