2011-08-08 17 views
13

Nuestra empresa está desarrollando una nueva aplicación, que tiene un objeto de datos empresariales algo grande en su núcleo. Decidimos probar Entity Framework con el código primero para abstraer la base de datos de la aplicación, pero las cosas han salido mal. El objeto comercial se compone de aproximadamente 60 clases, y en total alrededor de 600 propiedades; sin embargo, es una estructura de árbol y no hay punteros de cruce/retroceso presentes.Rendimiento extremadamente lento con Code-First con la versión de Entity Framework 4.1

Nuestra prueba consistió en agregar una única instancia no inicializada de la clase a la base de datos. Usar DbContext.Add en nuestra estructura de datos tomó 8 minutos en mi máquina de desarrollo. ¿Es este el rendimiento esperado de un objeto de este tamaño? ¿Hay una lista de problemas comunes que causa un rendimiento deficiente con Entity Framework? Siento que necesito ayuda con esto.

Algunos más puntos de datos: hay 27 elementos en el primer nivel debajo de la raíz del objeto comercial. Con 3 elementos presentes (el resto comentado), el tiempo para agregar es 4.5 segundos. Con 5 elementos presentes, es 11.8 segundos. Con 8 elementos presentes, es 1 minuto 12.5 segundos. Obviamente, el tamaño de estos elementos varía significativamente, pero estos parecen indicar un problema sistemático de algún tipo.

+3

¿Has mirado el SQL que está generando en Sql Profiler? A veces se puede deducir mucho sobre lo que EF está tratando de hacer mirando el sql. En mi opinión, salvar 60 clases relacionadas a la base de datos debe ser una operación secundaria de 1 segundo para EF. – automagic

+0

No lo habíamos probado, porque sabíamos que solo estaba llegando a la base de datos al final y todo el procesamiento estaba teniendo lugar dentro de EF. El rastro es muy grande ... No estoy muy seguro de lo que debería estar buscando. – dythim

+1

Intente configurar 'dbContext.Configuration.AutoDetectChangesEnabled = false;' cuando cree el contexto y mida de nuevo. Como dices 'Add' es lento (y no' SaveChanges', ¿no?) Sospecho que la detección automática de cambios/creación de instantáneas hace que las cosas vayan más despacio. – Slauma

Respuesta

3

Así que resolvió el problema. Mientras procedíamos con NHibernate, nos encontramos con un error estructural que era un código experimental. La categoría se suscribía a los eventos PropertyChanged de sus hijos, lo que provocó que NHibernate se bloqueara.

¡Qué es BUENO! Eso nos dijo que en realidad había un problema. Entity Framework se ejecutó para siempre sin ninguna indicación de que fuera un problema sobre el que pudiéramos hacer algo.

Así que de todos modos, vamos a seguir utilizando NHibernate por el momento. Nos gusta el control que tenemos sobre la estructura de la base de datos que obtenemos al usarlo.

4

... Nuestra prueba consistió en agregar una instancia única, no inicializada de la clase a la base de datos. Usando DbContext.Add ...

Quizás quiso asegurarse de que se crea el Código-Primer modelo y se carga en la memoria antes de que ha llamado Add? Me refiero a lo siguiente: Si se utiliza un código de prueba como esta ...

using (var context = new MyContext()) 
{ 
    var myHugeBusinessObject = CreateItSomeHow(); 

    context.HugeBusinessObjects.Add(myHugeBusinessObject); 

    context.SaveChanges(); 
} 

... y es la primera vez que se utiliza el contexto de la aplicación de prueba Add en realidad pasado algún tiempo para construir la Modelo EF en memoria antes de que comience a agregar el objeto al contexto.

Puede separar estos dos pasos, simplemente añadiendo un método ficticio antes de llamar Add, por ejemplo, algo como:

context.HugeBusinessObjects.Count(); 

He construido una prueba:

public class MyClass 
{ 
    public int Id { get; set; } 
    public string P1 { get; set; } 
    // ... P2 to P49 
    public string P50 { get; set; } 
    public MyClass Child1 { get; set; } 
    // ... Child1 to Child26 
    public MyClass Child27 { get; set; } 
} 

Con esta creé un objeto:

var my = new MyClass(); 
MyClass child = my; 
for (int i = 0; i < 100; i++) 
{ 
    child.Child1 = new MyClass(); 
    child = child.Child1; 
} 
child = my; 
for (int i = 0; i < 100; i++) 
{ 
    child.Child2 = new MyClass(); 
    child = child.Child2; 
} 
// and so on up to Child27 

Así que este gráfico de objetos tiene 2700 objetos secundarios con ingenio h 50 propiedades escalares cada una. Entonces probé este código:

using (var context = new MyContext()) 
{ 
    var my = CreateWithTheCodeAbove(); 

    context.MyClassSet.Count(); 
    context.MyClassSet.Add(my); 

    context.SaveChanges(); 
} 

...Count() (= la construcción del modelo EF) necesita aproximadamente 25 segundos. Add necesita 1 segundo. (Cambiar 100 a 1000 en los bucles de arriba (teniendo entonces 27000 objetos en el gráfico) aumenta el tiempo para Add de forma casi lineal a 9-10 segundos.) Este resultado es independiente de la configuración AutoDetectChangesEnabled a true o false.)

El siguiente resultado interesante: Si agrego 20 propiedades de navegación más (Child28Child47 a) a MyClass el tiempo pasado con la construcción del modelo (Count() en el código de ejemplo) explota a 140 segundos. La duración de Add solo aumenta linealmente con las propiedades adicionales.

Entonces, mi hipótesis es: en realidad no está midiendo el tiempo para agregar su objeto comercial al contexto, sino el momento en que EF necesita construir el modelo EF en la memoria. El tiempo para construir el modelo parece crecer exponencialmente con la complejidad del modelo: número de propiedades de navegación en una clase y quizás también el número de diferentes clases involucradas.

Para separar estos pasos, realice una prueba con alguna llamada ficticia como se sugirió anteriormente. Si ya tienes esta separación ... omg, olvida esta publicación.

+0

¡Tu sugerencia realmente mejoró mucho el rendimiento! El tiempo requerido disminuyó en una de las máquinas de nuestro otro desarrollador, de 5 minutos (el tiempo de mi computadora fue de 8 minutos, como en mi publicación inicial) a 2.5 minutos, ¡lo cual es una gran diferencia! Lamentablemente, todavía no está lista para producción ... Por el momento estamos empezando a mirar a NHibernate, aunque todavía estamos tratando de tener en cuenta a EF. Entiendo lo que usted dijo sobre el rezago que realmente proviene de la inicialización de la estructura interna de EF, pero no veo por qué separarlo como usted dijo debería marcar una gran diferencia. – dythim

+0

Trataré de hacer un modelo simplificado de nuestro código que pueda usarse para las pruebas (como lo sugirió Ladislav más arriba). – dythim

+0

@dythim: ¿Quiere decir que el * total * de tiempo ('Count() + Add()' se redujo a la mitad de las veces cuando solo se llamaba 'Add()'? Si es así, entonces eso es algo que tampoco esperaría. Esperaba que el tiempo fuera el mismo pero distribuido de manera desigual: 'Count()' (= creación del modelo) es mucho más lento que 'Add()'. ¿Revisó la hora de cada una de las llamadas y no solo la suma? La estructura del modelo interno solo se crea una vez por * instancia de la aplicación *. Si la creación del modelo es el problema de rendimiento, su pregunta pasa a otra (cómo mejorar el rendimiento de inicialización del modelo). – Slauma

Cuestiones relacionadas