framework c# entity-framework include where-clause

framework - linq include c#



EF: Incluir con where cláusula (2)

Puede consultar los objetos requeridos por

Context.Configuration.LazyLoadingEnabled = false; // Or: Context.Configuration.ProxyCreationEnabled = false; var buses = Context.Busses.Where(b => b.IsDriving) .Select(b => new { b, Passengers = b.Passengers .Where(p => p.Awake) }) .AsEnumerable() .Select(x => x.b) .ToList();

Lo que sucede aquí es que primero traes los autobuses que conducen y despiertas a los pasajeros de la base de datos. Luego, AsEnumerable() cambia de LINQ a Entidades a LINQ a objetos, lo que significa que los autobuses y pasajeros se materializarán y luego se procesarán en la memoria. Esto es importante porque sin él EF solo materializará la proyección final, Select(x => xb) , no los pasajeros.

Ahora EF tiene esta correlación de relación de características que se encarga de establecer todas las asociaciones entre los objetos que se materializan en el contexto. Esto significa que para cada Bus ahora solo se cargan sus pasajeros despiertos.

Cuando obtiene la colección de autobuses de ToList , tiene los autobuses con los pasajeros que desea y puede asignarlos con AutoMapper.

Esto solo funciona cuando la carga diferida está deshabilitada. De lo contrario, EF cargará perezosamente a todos los pasajeros de cada autobús cuando se acceda a los pasajeros durante la conversión a DTO.

Hay dos formas de desactivar la carga diferida. Al desactivar LazyLoadingEnabled se volverá a activar la carga LazyLoadingEnabled cuando se vuelva a habilitar. Al deshabilitar ProxyCreationEnabled se crearán entidades que no son capaces de cargar por sí solas , por lo que no iniciarán la carga ProxyCreationEnabled después de que ProxyCreationEnabled esté habilitado de nuevo. Esta puede ser la mejor opción cuando el contexto vive más tiempo que solo esta consulta.

Pero ... de muchos a muchos

Como se dijo, este trabajo conjunto depende de la reparación de la relación. Sin embargo, como explica Slauma , la Slauma relaciones no funciona con muchas asociaciones de muchos. Si Bus - Passenger es muchos-a-muchos, lo único que puede hacer es arreglarlo usted mismo:

Context.Configuration.LazyLoadingEnabled = false; // Or: Context.Configuration.ProxyCreationEnabled = false; var bTemp = Context.Busses.Where(b => b.IsDriving) .Select(b => new { b, Passengers = b.Passengers .Where(p => p.Awake) }) .ToList(); foreach(x in bTemp) { x.b.Pasengers = x.Passengers; } var busses = bTemp.Select(x => x.b).ToList();

... y todo se vuelve aún menos atractivo.

Herramientas de terceros

Hay una biblioteca, EntityFramework.DynamicFilters que hace esto mucho más fácil. Le permite definir filtros globales para las entidades, que posteriormente se aplicarán cada vez que se consulte a la entidad. En tu caso, esto podría verse así:

modelBuilder.Filter("Awake", (Person p) => p.Awake, true);

Ahora si lo haces ...

Context.Busses.Where(b => b.IsDriving) .Include(b => b.People)

... verá que el filtro se aplica a la colección incluida.

También puede habilitar / deshabilitar filtros, para que tenga control sobre cuándo se aplican. Creo que esta es una biblioteca muy ordenada.

Hay una biblioteca similar del fabricante de AutoMapper: EntityFramework.Filters

Núcleo de Entity Framework

Desde la versión 2.0.0, EF-core tiene filtros de consulta de nivel de modelo . Aunque esta es una gran adición a sus características, hasta ahora la limitación es que no se puede aplicar a las propiedades de navegación, solo a la entidad raíz de una consulta. Esperemos que en una versión posterior estos filtros logren un uso más amplio.

Como el título sugiere, estoy buscando una forma de hacer una cláusula Where en combinación con un include.

Aquí está mi situación: soy responsable del soporte de una aplicación grande llena de olores de código. Cambiar demasiados códigos causa errores en todas partes, así que estoy buscando la solución más segura.

Digamos que tengo un objeto Bus y un objeto People (Bus tiene una colección de navegación de prop de navegación). En mi consulta, necesito seleccionar todos los autobuses con solo los pasajeros que están despiertos. Este es un ejemplo ficticio simplista

En el código actual:

var busses = Context.Busses.Where(b=>b.IsDriving == true); foreach(var bus in busses) { var passengers = Context.People.Where(p=>p.BusId == bus.Id && p.Awake == true); foreach(var person in passengers) { bus.Passengers.Add(person); } }

Después de este código, se elimina el contexto y, en el método de llamada, las entidades de bus resultantes se asignan a una clase de DTO (copia del 100% de la entidad).

Este código causa llamadas múltiples a DB que es un No-Go, así que encontré esta solución en MSDN Blogs

Esto funcionó muy bien al depurar el resultado, pero cuando las entidades se mapean al DTO (usando AutoMapper) recibo una excepción de que el Contexto / Conexión se ha cerrado y que el objeto no se puede cargar. (El contexto siempre está cerrado no puede cambiar esto :()

Por lo tanto, debo asegurarme de que los Pasajeros seleccionados ya estén cargados (IsLoaded en la propiedad de navegación también es falso). Si inspecciono la colección de Pasajeros, el Conteo también arroja la Excepción, pero también hay una colección en la Colección de Passegadores llamada "entidades relacionadas envueltas" que contienen mis objetos filtrados.

¿Hay alguna manera de cargar estas entidades relacionadas envolventes en toda la colección? (No puedo cambiar la configuración de asignación de Automapper porque esta se usa en toda la aplicación).

¿Hay alguna otra manera de obtener los pasajeros activos?

Cualquier pista es bienvenida ...

Editar

La respuesta de Gert Arnold no funciona porque los datos no se cargan con entusiasmo. Pero cuando lo simplifico y borro el lugar donde está cargado. Esto es realmente extraño ya que la ejecución sql devuelve a todos los pasajeros en ambos casos. Entonces debe haber un problema al volver a poner los resultados en la entidad.

Context.Configuration.LazyLoadingEnabled = false; var buses = Context.Busses.Where(b => b.IsDriving) .Select(b => new { b, Passengers = b.Passengers }) .ToList() .Select(x => x.b) .ToList();

Edit2

¡Después de mucha lucha la respuesta de Gert Arnold funciona! Como Gert Arnold sugirió que necesita desactivar la carga diferida y mantenerlo desactivado. Esto pedirá algunos cambios adicionales a la aplicación, ya que el desarrollador anterior adoraba la Carga diferida -_-


Descargo de responsabilidad : soy el propietario del proyecto Entity Framework Plus

La función EF + Query IncludeFilter permite filtrar entidades relacionadas.

var buses = Context.Busses .Where(b => b.IsDriving) .IncludeFilter(x => x.Passengers.Where(p => p.Awake)) .ToList();

Wiki: EF + Query IncludeFilter