Solución:
Esta característica ahora se ha agregado a Entity Framework core 5. Para versiones anteriores, necesita una solución alternativa (tenga en cuenta que EF6 es una versión anterior).
Solución alternativa de Entity Framework 6
En EF6, una solución alternativa es consultar primero los objetos necesarios en una proyección (new
) y dejar que la reparación de la relación haga su trabajo.
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 busca los autobuses que conducen y despierta a los pasajeros de la base de datos. Luego, AsEnumerable()
cambia de LINQ a Entidades a LINQ a objetos, lo que significa que los buses y pasajeros serán materializados y luego procesados en memoria. Esto es importante porque sin él EF solo materializará la proyección final, Select(x => x.b)
, no los pasajeros.
Ahora EF tiene esta característica arreglo de la relación que se encarga de establecer todas las asociaciones entre objetos que se materializan en el contexto. Esto significa que para cada Bus
ahora sólo se cargan sus pasajeros despiertos.
Cuando consiga la recogida de autobuses por ToList
tienes los autobuses con los pasajeros que deseas y puedes mapearlos con AutoMapper.
Esto solo funciona cuando la carga diferida está desactivada. De lo contrario, EF cargará de forma diferida todos pasajeros para cada autobús cuando se accede a los pasajeros durante la conversión a DTO.
Hay dos formas de desactivar la carga diferida. Inhabilitando LazyLoadingEnabled
reactivará la carga diferida cuando se habilite de nuevo. Inhabilitando ProxyCreationEnabled
creará entidades que no son capaces de carga diferida ellos mismos, para que no comiencen a cargarse de forma diferida después ProxyCreationEnabled
está habilitado de nuevo. Esta puede ser la mejor opción cuando el contexto dura más tiempo que esta única consulta.
Pero … muchos a muchos
Como se dijo, esta solución se basa en la reparación de la relación. Sin embargo, como explica Slauma aquí, la reparación de relaciones no funciona con asociaciones de muchos a muchos. Si Bus
–Passenger
es de varios a varios, lo único que puede hacer es solucionarlo 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 que esto sea mucho más fácil. Permite definir filtros globales para entidades, que posteriormente se aplicarán cada vez que se consulte la entidad. En su 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, por lo que tiene control sobre cuándo se aplican. Creo que esta es una biblioteca muy ordenada.
Existe una biblioteca similar del fabricante de AutoMapper: EntityFramework.Filters
Solución alternativa del núcleo de Entity Framework
Desde la versión 2.0.0, EF-core tiene filtros de consulta globales. Estos se pueden utilizar para establecer un filtro predefinido en las entidades que se incluirán. Por supuesto, eso no ofrece la misma flexibilidad que el filtrado. Include
sobre la marcha. Aunque los filtros de consultas globales son una gran característica, hasta ahora la limitación es que un filtro no puede contener referencias a las propiedades de navegación, solo a la entidad raíz de una consulta. Es de esperar que en una versión posterior estos filtros alcancen un uso más amplio.
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
Ahora EF Core 5.0El método de inclusión de filtros ahora admite el filtrado de las entidades incluidas
var busses = _Context.Busses
.Include(b => b.Passengers
.Where(p => p.Awake))
.Where(b => b.IsDriving);