Impacto en el rendimiento de Entity Framework Core 3.0 por incluir propiedades de navegación de colección (explosión cartesiana)

8

Nos enfrentamos a un importante problema de rendimiento después de actualizar EF Core 2.2 a EF Core 3.0. Imagine un modelo de datos simple con una propiedad de navegación de colección única y cientos de campos (la realidad se ve aún más oscura):

public class Item
{
  [Key]
  public int ItemID {get;set;}

  public ICollection<AddInfo> AddInfos {get;set;}
  ...  // consisting of another 100+ properties!
}

y

public class AddInfo
{
  [Key]
  public int AddInfoID {get;set;}
  public int? ItemID {get;set;}
  public string SomePayload {get;set;}
}

Durante la recuperación del elemento, consultamos de la siguiente manera:

...
var myQueryable = this._context.Items.Include(i => i.AddInfos).Where(**some filter**);
... // moar filters
var result = myQueryable.ToList();

Directo, hasta este punto.

En EF 2.2, obtener esos resultados consultables en dos consultas separadas, una para Itemy otra para el AddInfonivel. Estas consultas suelen obtener 10.000 itemsy alrededor de 250.000 AddInfos.

Sin embargo, en EF Core 3.0, se genera una única consulta, unir AddInfoa la izquierda a Item esa a primera vista parece ser la mejor opción. ItemSin embargo, nuestro debe ser recuperado con todos los más de 100 campos, por lo que no es factible proyectar a una clase más pequeña o tipo anónimo (agregar una llamada al método .Select (...)). Por lo tanto, el conjunto de resultados tiene tanta redundancia (cada uno Itemaproximadamente 25 veces) que la consulta en sí misma tarda demasiado en ejecutarse en un tiempo aceptable.

¿EF-Core 3.0 proporciona alguna opción que nos permita volver al comportamiento de consulta del viejo y bueno EF Core 2.2 veces sin grandes cambios en nuestro modelo de datos? Ya nos estamos beneficiando de este cambio en otras partes de la aplicación, pero no en este escenario particular.

¡Muchas gracias de antemano!

Actualizar

Después de una investigación más profunda, descubrí que este problema ya se aborda con Microsoft aquí y fuera de la caja, parece que no hay forma de configurar la ejecución de la consulta dividida.

TheSchmu
fuente
¿La consulta se realiza a través de una conexión web (http)? Los encabezados predeterminados pueden ser diferentes en Core 2.2 y Core 3.0 (como http versión 1.0 y http versión 1.1). Es posible que desee utilizar un sniffer como wireshark o fiddler y verificar la primera solicitud para verificar que los encabezados sean los mismos.
jdweng
1
Lee esta advertencia . Tiene razón, el comportamiento ha cambiado, coincide con el antiguo comportamiento de EF 6.x. Lo que no puedo entender ¿Cómo fue que EF 2.2 lo tradujo a solo 2 consultas? ¿Tienes muestras para eso?
Eldar
@Eldar, gracias por proporcionar el enlace. La traducción a dos consultas fue posible porque el mismo filtro (-> WHERE-Clause) se usó para obtener datos de la tabla Item y AddInfo, esta última, por supuesto, con una unión de vuelta a la tabla de elementos en sí. EF volvió a unir los elementos y las propiedades de navegación de su colección en la memoria.
TheSchmu

Respuestas:

3

Según la actualización de mi pregunta inicial, las ideas fueron tan lejos como para asegurarme de que actualmente, de hecho, no hay una configuración integrada para volver a la ejecución de consultas divididas.

Sin embargo, MS proporcionó ejemplos de código sobre cómo hacer esto con cambios mínimos de código (¡para nuestro caso de uso!) Aquí .

Simplemente estamos eliminando las llamadas .Include (...) a las propiedades de navegación de la colección (las relaciones 1: n en nuestro caso, ¡las relaciones 1: 1 no se ven afectadas!). Después de buscar los elementos, simplemente estamos haciendo otra llamada usando:

...
var myQueryable = this._context.Items.Where(**some filter**);
... // moar filters
var result = myQueryable.ToList();
...
var addInfos = myQueryable.Include(x => x.AddInfos).SelectMany(x => x.AddInfos).Select(x => new {x.ItemID, x}).ToList();

Esto recupera las entidades de propiedad de navegación de la colección y, si el seguimiento de cambios está habilitado, llena automáticamente las colecciones en los elementos individuales de la resultvariable.

TheSchmu
fuente
¿Puede compartir la salida (consulta sql generada) de la segunda consulta también?
Eldar