2009-03-26 21 views
16

El executeTime a continuación es 30 segundos la primera vez, y 25 segundos la próxima vez que ejecuto el mismo conjunto de código. Cuando miro en el Analizador de SQL, inmediatamente veo un inicio de sesión, luego permanece allí por unos 30 segundos. Luego, tan pronto como se ejecuta la instrucción de selección, la aplicación finaliza el comando ToList. Cuando ejecuto la consulta generada desde Management Studio, la consulta de la base de datos solo toma alrededor de 400 ms. Devuelve 14 filas y 350 columnas. Parece que el tiempo que lleva transformar los resultados de la base de datos a las entidades es tan pequeño que no es notorio.¿Por qué Entity Framework tarda 30 segundos en cargar registros cuando la consulta generada solo tarda 1/2 segundo?

Así que lo que está sucediendo en los 30 segundos antes de la llamada base de datos se hace?

Si marco de la entidad es este lento, no es posible que nosotros utilizamos. ¿Hay algo que estoy haciendo mal o algo que puedo cambiar para acelerar esto dramáticamente?

ACTUALIZACIÓN: bien, si uso una consulta compilada, la primera vez que se tarda 30 segundos, y la segunda vez que se necesita 1/cuarto de segundo. ¿Hay algo que pueda hacer para acelerar la primera llamada?

using (EntitiesContext context = new EntitiesContext()) 
{ 
    Stopwatch sw = new Stopwatch(); 
    sw.Start(); 
    var groupQuery = (from g in context.Groups.Include("DealContract") 
        .Include("DealContract.Contracts") 
        .Include("DealContract.Contracts.AdvertiserAccountType1") 
        .Include("DealContract.Contracts.ContractItemDetails") 
        .Include("DealContract.Contracts.Brands") 
        .Include("DealContract.Contracts.Agencies") 
        .Include("DealContract.Contracts.AdvertiserAccountType2") 
        .Include("DealContract.Contracts.ContractProductLinks.Products") 
        .Include("DealContract.Contracts.ContractPersonnelLinks") 
        .Include("DealContract.Contracts.ContractSpotOrderTypes") 
        .Include("DealContract.Contracts.Advertisers") 
       where g.GroupKey == 6 
       select g).OfType<Deal>(); 
    sw.Stop(); 
    var queryTime = sw.Elapsed; 
    sw.Reset(); 
    sw.Start(); 
    var groups = groupQuery.ToList(); 
    sw.Stop(); 
    var executeTime = sw.Elapsed; 
} 

Respuesta

12

Tuve exactamente el mismo problema, mi consulta tardaba 40 segundos.

he encontrado el problema era con los .Include("table_name") funciones. Mientras más de estos tenía, peor era.En lugar de eso, cambié mi código a Lazy Load, todos los datos que necesitaba justo después de la consulta, esto redujo el tiempo total a aproximadamente 1,5 segundos desde 40 segundos. Hasta donde yo sé, esto logra exactamente lo mismo.

Así que para su código sería algo como esto:

var groupQuery = (from g in context.Groups 
      where g.GroupKey == 6 
      select g).OfType<Deal>(); 

var groups = groupQuery.ToList(); 

foreach (var g in groups) 
{ 
    // Assuming Dealcontract is an Object, not a Collection of Objects 
    g.DealContractReference.Load(); 
    if (g.DealContract != null) 
    { 
     foreach (var d in g.DealContract) 
     { 
      // If the Reference is to a collection, you can just to a Straight ".Load" 
      // if it is an object, you call ".Load" on the refence instead like with "g.DealContractReference" above 
      d.Contracts.Load(); 
      foreach (var c in d.Contracts) 
      { 
       c.AdvertiserAccountType1Reference.Load(); 
       // etc.... 
      } 
     } 
    } 
} 

Por cierto, si se va a agregar esta línea de código por encima de la consulta en su código actual, sería golpear el tiempo de inactividad hasta aproximadamente 4 -5 segundos (todavía demasiado ling en mi opción) por lo que entiendo, la opción MergeOption.NoTracking desactiva una gran cantidad de la sobrecarga de seguimiento para la actualización y la inserción de las cosas de nuevo en la base de datos:

context.groups.MergeOption = MergeOption.NoTracking; 
+3

Esto parece tan contra intuitivo. Hacer múltiples solicitudes de SQL y cargar la misma cantidad de objetos es más rápido que hacer una solicitud SQL. – toxaq

+0

Chris, parece que sabes mucho sobre .include, ¿puedes echar un vistazo a mi publicación? Http://stackoverflow.com/questions/10320174/speed-up-return-of-linq-entity-result –

+0

@bugz - Eché un vistazo. Lo siento, hombre, no tengo idea. Debo señalar que al final estaba extremadamente insatisfecho con ASP.net y el marco de la entidad. Todo lo que hice tomó demasiado tiempo. Actualmente estoy volviendo a escribir esa aplicación completa en el motor de la aplicación de Google. –

4

Es debido al Include. Supongo que estás ansioso por cargar muchos objetos en la memoria. Lleva mucho tiempo construir los objetos C# que corresponden a sus entidades de db.

Mi recomendación para usted es tratar de carga perezosa sólo los datos que necesita.

+0

Lazy Loading wouldnt ayudar porque necesitamos toda de los objetos incluidos Realmente solo devuelve unos 40 objetos en total (1 Deal, 3 DealContracts, 3 Contracts, 3 detalles contractuales en cada contrato, y 1 de cada una de las otras propiedades en cada contrato), así que no creo que sea demasiado intenso ...? – NotDan

+0

No estoy de acuerdo con que la necesidad de todos los objetos significa que la carga lenta no ayudaría. Ver mi respuesta por la razón. A veces es más rápido ejecutar una consulta más simple y luego dejar que los detalles con otras consultas más simples. –

0

EF tarda un tiempo en arrancar. Necesita generar metadatos a partir de xml y probablemente genere objetos usados ​​para el mapeo. Por lo tanto, tarda unos segundos en iniciarse, no creo que haya una forma de evitarlo, excepto que nunca reinicie su programa.

+2

Realmente, ¿necesita 30 segundos para comenzar? Eso parece excesivo. – billb

+0

No lo he usado realmente con ningún modelo complicado, y la mayoría de las veces toma alrededor de 5 segundos. Tus objetos se ven mucho más complejos que aquellos que tengo. Intenta hacer una consulta más pequeña y ver cuánto tiempo lleva. – AndreasN

+0

Agregando una consulta simple (1 registro, no incluye) antes de que la consulta complicada ahorre 2 segundos. Parece que le toma a EF 2 segundos comenzar. Todavía demora 28 segundos para la primera consulta complicada, que es demasiado larga. – NotDan

2

La única manera de hacer la compilación inicial de la consulta más rápido que yo sepa es hacer la consulta menos complejo. La documentación de MSDN en performance considerations for the Entity Framework y Compiled Queries no indica que hay alguna forma de guardar una consulta compilada para su uso en una sesión de ejecución de aplicación diferente.

Me gustaría añadir que hemos encontrado que tener muchas inclusiones puede hacer que la ejecución de consultas sea más lenta que tener menos inclusiones y hacer más cargas en entidades relacionadas más tarde. Se requiere algo de prueba y error para encontrar el medio adecuado.

Sin embargo, tengo que preguntar si realmente necesita todas las propiedades de cada entidad va a incluir aquí. Me parece que hay una gran cantidad de tipos de entidades diferentes en esta consulta, por lo que su materialización podría ser bastante costoso. Si solo está tratando de obtener resultados tabulares que no tiene la intención de actualizar, proyectar el (relativamente) menor número de campos que realmente necesita en un tipo plano y anónimo debería ser significativamente más rápido por varias razones. Además, esto le libera de tener que preocuparse por la carga ansiosa, llamando a Load/IsLoaded, etc.

Sin duda, puede acelerar la generación de la vista inicial precompilando las vistas de entidad. Hay documentation on MSDN for this. Pero como paga ese costo en el momento en que se ejecuta la primera consulta, su prueba con una consulta simple muestra que esto se ejecuta en los próximos 2 segundos para usted. Es agradable decir que 2 segundos, pero no salvará nada más.

+0

Desgraciadamente, realmente necesito todas las propiedades en todos los objetos. – NotDan

Cuestiones relacionadas